#include <stdio.h>
#include <time.h>
#include "ap.h"
// disable some irrelevant warnings
#if (AE_COMPILER==AE_MSVC)
#pragma warning(disable:4100)
#pragma warning(disable:4127)
#pragma warning(disable:4702)
#pragma warning(disable:4996)
#endif
#include "alglibmisc.h"
#include "alglibinternal.h"
#include "linalg.h"
#include "statistics.h"
#include "dataanalysis.h"
#include "specialfunctions.h"
#include "solvers.h"
#include "optimization.h"
#include "diffequations.h"
#include "fasttransforms.h"
#include "integration.h"
#include "interpolation.h"

using namespace alglib_impl;






ae_bool testhqrnd(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing tag sort
*************************************************************************/
ae_bool testtsort(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing Nearest Neighbor Search
*************************************************************************/
ae_bool testnearestneighbor(ae_bool silent, ae_state *_state);








ae_bool testablas(ae_bool silent, ae_state *_state);








ae_bool testbasestat(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing BDSS operations
*************************************************************************/
ae_bool testbdss(ae_bool silent, ae_state *_state);








ae_bool testdforest(ae_bool silent, ae_state *_state);








ae_bool testblas(ae_bool silent, ae_state *_state);








ae_bool testkmeans(ae_bool silent, ae_state *_state);








ae_bool testhblas(ae_bool silent, ae_state *_state);








ae_bool testreflections(ae_bool silent, ae_state *_state);








ae_bool testcreflections(ae_bool silent, ae_state *_state);








ae_bool testsblas(ae_bool silent, ae_state *_state);








/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testortfac(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing symmetric EVD subroutine
*************************************************************************/
ae_bool testevd(ae_bool silent, ae_state *_state);








ae_bool testmatgen(ae_bool silent, ae_state *_state);








ae_bool testtrfac(ae_bool silent, ae_state *_state);








/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testtrlinsolve(ae_bool silent, ae_state *_state);








/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testsafesolve(ae_bool silent, ae_state *_state);








ae_bool testrcond(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testmatinv(ae_bool silent, ae_state *_state);








ae_bool testlda(ae_bool silent, ae_state *_state);








ae_bool testgammafunc(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testbdsvd(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing SVD decomposition subroutine
*************************************************************************/
ae_bool testsvd(ae_bool silent, ae_state *_state);








ae_bool testlinreg(ae_bool silent, ae_state *_state);








ae_bool testxblas(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testdensesolver(ae_bool silent, ae_state *_state);








ae_bool testlinmin(ae_bool silent, ae_state *_state);








ae_bool testmincg(ae_bool silent, ae_state *_state);


/*************************************************************************
Other properties
*************************************************************************/
void testother(ae_bool* err, ae_state *_state);








ae_bool testminbleic(ae_bool silent, ae_state *_state);








ae_bool testmcpd(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing
*************************************************************************/
ae_bool testfbls(ae_bool silent, ae_state *_state);








ae_bool testminlbfgs(ae_bool silent, ae_state *_state);








ae_bool testmlptrain(ae_bool silent, ae_state *_state);








ae_bool testmlpe(ae_bool silent, ae_state *_state);








ae_bool testpca(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testodesolver(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testfft(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testconv(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testcorr(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testfht(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testgq(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testgkq(ae_bool silent, ae_state *_state);








/*************************************************************************
Test
*************************************************************************/
ae_bool testautogk(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing IDW interpolation
*************************************************************************/
ae_bool testidwint(ae_bool silent, ae_state *_state);








ae_bool testratint(ae_bool silent, ae_state *_state);








/*************************************************************************
Unit test
*************************************************************************/
ae_bool testpolint(ae_bool silent, ae_state *_state);








ae_bool testspline1d(ae_bool silent, ae_state *_state);








ae_bool testminlm(ae_bool silent, ae_state *_state);








ae_bool testlsfit(ae_bool silent, ae_state *_state);








ae_bool testpspline(ae_bool silent, ae_state *_state);








ae_bool testspline2d(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testspdgevd(ae_bool silent, ae_state *_state);








ae_bool testinverseupdate(ae_bool silent, ae_state *_state);








/*************************************************************************
Testing Schur decomposition subroutine
*************************************************************************/
ae_bool testschur(ae_bool silent, ae_state *_state);








ae_bool testnleq(ae_bool silent, ae_state *_state);








ae_bool testchebyshev(ae_bool silent, ae_state *_state);








ae_bool testhermite(ae_bool silent, ae_state *_state);








ae_bool testlaguerre(ae_bool silent, ae_state *_state);








ae_bool testlegendre(ae_bool silent, ae_state *_state);





typedef struct
{
    ae_bool bfield;
    double rfield;
    ae_int_t ifield;
    ae_complex cfield;
    ae_vector b1field;
    ae_vector r1field;
    ae_vector i1field;
    ae_vector c1field;
    ae_matrix b2field;
    ae_matrix r2field;
    ae_matrix i2field;
    ae_matrix c2field;
} rec1;


typedef struct
{
    ae_vector b;
    ae_vector i;
    ae_vector r;
} rec4serialization;





void rec4serializationalloc(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state);


void rec4serializationserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state);


void rec4serializationunserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state);


ae_bool testalglibbasics(ae_bool silent, ae_state *_state);
ae_bool _rec1_init(rec1* p, ae_state *_state, ae_bool make_automatic);
ae_bool _rec1_init_copy(rec1* dst, rec1* src, ae_state *_state, ae_bool make_automatic);
void _rec1_clear(rec1* p);
ae_bool _rec4serialization_init(rec4serialization* p, ae_state *_state, ae_bool make_automatic);
ae_bool _rec4serialization_init_copy(rec4serialization* dst, rec4serialization* src, ae_state *_state, ae_bool make_automatic);
void _rec4serialization_clear(rec4serialization* p);




static void testhqrndunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state);
static void testhqrndunit_unsetstate(hqrndstate* state, ae_state *_state);





ae_bool testhqrnd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t samplesize;
    double sigmathreshold;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t i;
    ae_int_t pass;
    ae_int_t s1;
    ae_int_t s2;
    ae_int_t i1;
    ae_int_t i2;
    double r1;
    double r2;
    ae_vector x;
    double mean;
    double means;
    double stddev;
    double stddevs;
    double lambdav;
    ae_bool seederrors;
    ae_bool urerrors;
    double ursigmaerr;
    ae_bool uierrors;
    double uisigmaerr;
    ae_bool normerrors;
    double normsigmaerr;
    ae_bool experrors;
    double expsigmaerr;
    hqrndstate state;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    _hqrndstate_init(&state, _state, ae_true);

    waserrors = ae_false;
    sigmathreshold = 7;
    samplesize = 100000;
    passcount = 50;
    seederrors = ae_false;
    urerrors = ae_false;
    uierrors = ae_false;
    normerrors = ae_false;
    experrors = ae_false;
    ae_vector_set_length(&x, samplesize-1+1, _state);
    
    /*
     * Test seed errors
     */
    for(pass=1; pass<=passcount; pass++)
    {
        s1 = 1+ae_randominteger(32000, _state);
        s2 = 1+ae_randominteger(32000, _state);
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        i1 = hqrnduniformi(&state, 100, _state);
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        i2 = hqrnduniformi(&state, 100, _state);
        seederrors = seederrors||i1!=i2;
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        r1 = hqrnduniformr(&state, _state);
        testhqrndunit_unsetstate(&state, _state);
        hqrndseed(s1, s2, &state, _state);
        r2 = hqrnduniformr(&state, _state);
        seederrors = seederrors||ae_fp_neq(r1,r2);
    }
    
    /*
     * Test HQRNDRandomize() and real uniform generator
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    ursigmaerr = 0;
    for(i=0; i<=samplesize-1; i++)
    {
        x.ptr.p_double[i] = hqrnduniformr(&state, _state);
    }
    for(i=0; i<=samplesize-1; i++)
    {
        urerrors = (urerrors||ae_fp_less_eq(x.ptr.p_double[i],0))||ae_fp_greater_eq(x.ptr.p_double[i],1);
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,0) )
    {
        ursigmaerr = ae_maxreal(ursigmaerr, ae_fabs((mean-0.5)/means, _state), _state);
    }
    else
    {
        urerrors = ae_true;
    }
    if( ae_fp_neq(stddevs,0) )
    {
        ursigmaerr = ae_maxreal(ursigmaerr, ae_fabs((stddev-ae_sqrt((double)1/(double)12, _state))/stddevs, _state), _state);
    }
    else
    {
        urerrors = ae_true;
    }
    urerrors = urerrors||ae_fp_greater(ursigmaerr,sigmathreshold);
    
    /*
     * Test HQRNDRandomize() and integer uniform
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    uisigmaerr = 0;
    for(n=2; n<=10; n++)
    {
        for(i=0; i<=samplesize-1; i++)
        {
            x.ptr.p_double[i] = hqrnduniformi(&state, n, _state);
        }
        for(i=0; i<=samplesize-1; i++)
        {
            uierrors = (uierrors||ae_fp_less(x.ptr.p_double[i],0))||ae_fp_greater_eq(x.ptr.p_double[i],n);
        }
        testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
        if( ae_fp_neq(means,0) )
        {
            uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((mean-0.5*(n-1))/means, _state), _state);
        }
        else
        {
            uierrors = ae_true;
        }
        if( ae_fp_neq(stddevs,0) )
        {
            uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((stddev-ae_sqrt((ae_sqr(n, _state)-1)/12, _state))/stddevs, _state), _state);
        }
        else
        {
            uierrors = ae_true;
        }
    }
    uierrors = uierrors||ae_fp_greater(uisigmaerr,sigmathreshold);
    
    /*
     * Special 'close-to-limit' test on uniformity of integers
     * (straightforward implementation like 'RND mod N' will return
     *  non-uniform numbers for N=2/3*LIMIT)
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    uisigmaerr = 0;
    n = 1431655708;
    for(i=0; i<=samplesize-1; i++)
    {
        x.ptr.p_double[i] = hqrnduniformi(&state, n, _state);
    }
    for(i=0; i<=samplesize-1; i++)
    {
        uierrors = (uierrors||ae_fp_less(x.ptr.p_double[i],0))||ae_fp_greater_eq(x.ptr.p_double[i],n);
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,0) )
    {
        uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((mean-0.5*(n-1))/means, _state), _state);
    }
    else
    {
        uierrors = ae_true;
    }
    if( ae_fp_neq(stddevs,0) )
    {
        uisigmaerr = ae_maxreal(uisigmaerr, ae_fabs((stddev-ae_sqrt((ae_sqr(n, _state)-1)/12, _state))/stddevs, _state), _state);
    }
    else
    {
        uierrors = ae_true;
    }
    uierrors = uierrors||ae_fp_greater(uisigmaerr,sigmathreshold);
    
    /*
     * Test normal
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    normsigmaerr = 0;
    i = 0;
    while(i<samplesize)
    {
        hqrndnormal2(&state, &r1, &r2, _state);
        x.ptr.p_double[i] = r1;
        if( i+1<samplesize )
        {
            x.ptr.p_double[i+1] = r2;
        }
        i = i+2;
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,0) )
    {
        normsigmaerr = ae_maxreal(normsigmaerr, ae_fabs((mean-0)/means, _state), _state);
    }
    else
    {
        normerrors = ae_true;
    }
    if( ae_fp_neq(stddevs,0) )
    {
        normsigmaerr = ae_maxreal(normsigmaerr, ae_fabs((stddev-1)/stddevs, _state), _state);
    }
    else
    {
        normerrors = ae_true;
    }
    normerrors = normerrors||ae_fp_greater(normsigmaerr,sigmathreshold);
    
    /*
     * Test exponential
     */
    testhqrndunit_unsetstate(&state, _state);
    hqrndrandomize(&state, _state);
    expsigmaerr = 0;
    lambdav = 2+5*ae_randomreal(_state);
    for(i=0; i<=samplesize-1; i++)
    {
        x.ptr.p_double[i] = hqrndexponential(&state, lambdav, _state);
    }
    for(i=0; i<=samplesize-1; i++)
    {
        uierrors = uierrors||ae_fp_less(x.ptr.p_double[i],0);
    }
    testhqrndunit_calculatemv(&x, samplesize, &mean, &means, &stddev, &stddevs, _state);
    if( ae_fp_neq(means,0) )
    {
        expsigmaerr = ae_maxreal(expsigmaerr, ae_fabs((mean-1.0/lambdav)/means, _state), _state);
    }
    else
    {
        experrors = ae_true;
    }
    if( ae_fp_neq(stddevs,0) )
    {
        expsigmaerr = ae_maxreal(expsigmaerr, ae_fabs((stddev-1.0/lambdav)/stddevs, _state), _state);
    }
    else
    {
        experrors = ae_true;
    }
    experrors = experrors||ae_fp_greater(expsigmaerr,sigmathreshold);
    
    /*
     * Final report
     */
    waserrors = (((seederrors||urerrors)||uierrors)||normerrors)||experrors;
    if( !silent )
    {
        printf("RNG TEST\n");
        printf("SEED TEST:                               ");
        if( !seederrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("UNIFORM CONTINUOUS:                      ");
        if( !urerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("UNIFORM INTEGER:                         ");
        if( !uierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("NORMAL:                                  ");
        if( !normerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("EXPONENTIAL:                             ");
        if( !experrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testhqrndunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state)
{
    ae_int_t i;
    double v1;
    double v2;
    double variance;

    *mean = 0;
    *means = 0;
    *stddev = 0;
    *stddevs = 0;

    *mean = 0;
    *means = 1;
    *stddev = 0;
    *stddevs = 1;
    variance = 0;
    if( n<=1 )
    {
        return;
    }
    
    /*
     * Mean
     */
    for(i=0; i<=n-1; i++)
    {
        *mean = *mean+x->ptr.p_double[i];
    }
    *mean = *mean/n;
    
    /*
     * Variance (using corrected two-pass algorithm)
     */
    if( n!=1 )
    {
        v1 = 0;
        for(i=0; i<=n-1; i++)
        {
            v1 = v1+ae_sqr(x->ptr.p_double[i]-(*mean), _state);
        }
        v2 = 0;
        for(i=0; i<=n-1; i++)
        {
            v2 = v2+(x->ptr.p_double[i]-(*mean));
        }
        v2 = ae_sqr(v2, _state)/n;
        variance = (v1-v2)/(n-1);
        if( ae_fp_less(variance,0) )
        {
            variance = 0;
        }
        *stddev = ae_sqrt(variance, _state);
    }
    
    /*
     * Errors
     */
    *means = *stddev/ae_sqrt(n, _state);
    *stddevs = *stddev*ae_sqrt(2, _state)/ae_sqrt(n-1, _state);
}


/*************************************************************************
Unsets HQRNDState structure
*************************************************************************/
static void testhqrndunit_unsetstate(hqrndstate* state, ae_state *_state)
{


    state->s1 = 0;
    state->s2 = 0;
    state->v = 0;
    state->magicv = 0;
}



static void testtsortunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state);
static void testtsortunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state);
static void testtsortunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state);
static void testtsortunit_testsortresults(/* Real    */ ae_vector* asorted,
     /* Integer */ ae_vector* p1,
     /* Integer */ ae_vector* p2,
     /* Real    */ ae_vector* aoriginal,
     ae_int_t n,
     ae_bool* waserrors,
     ae_state *_state);





/*************************************************************************
Testing tag sort
*************************************************************************/
ae_bool testtsort(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t n;
    ae_int_t i;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_vector a;
    ae_vector a0;
    ae_vector a1;
    ae_vector a2;
    ae_vector a3;
    ae_vector ar;
    ae_vector ai;
    ae_vector p1;
    ae_vector p2;
    ae_vector bufr1;
    ae_vector bufr2;
    ae_vector bufi1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ar, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ai, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&bufr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bufr2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bufi1, 0, DT_INT, _state, ae_true);

    waserrors = ae_false;
    maxn = 100;
    passcount = 10;
    
    /*
     * Test tagsort
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * (probably) distinct sort:
             * * first sort A0 using TagSort and test sort results
             * * now we can use A0 as reference point and test other functions
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                ar.ptr.p_double[i] = i;
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]))||ai.ptr.p_int[i]!=p1.ptr.p_int[i];
            }
            tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]))||ae_fp_neq(ar.ptr.p_double[i],p1.ptr.p_int[i]);
            }
            tagsortfast(&a3, &bufr1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            
            /*
             * non-distinct sort
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = i/2;
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                ar.ptr.p_double[i] = i;
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]))||ai.ptr.p_int[i]!=p1.ptr.p_int[i];
            }
            tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]))||ae_fp_neq(ar.ptr.p_double[i],p1.ptr.p_int[i]);
            }
            tagsortfast(&a3, &bufr1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            
            /*
             * 'All same' sort
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = 0;
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                ar.ptr.p_double[i] = i;
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]))||ai.ptr.p_int[i]!=p1.ptr.p_int[i];
            }
            tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]))||ae_fp_neq(ar.ptr.p_double[i],p1.ptr.p_int[i]);
            }
            tagsortfast(&a3, &bufr1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
            }
            
            /*
             * 0-1 sort
             */
            testtsortunit_unset1di(&p1, _state);
            testtsortunit_unset1di(&p2, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&a0, n, _state);
            ae_vector_set_length(&a1, n, _state);
            ae_vector_set_length(&a2, n, _state);
            ae_vector_set_length(&a3, n, _state);
            ae_vector_set_length(&ar, n, _state);
            ae_vector_set_length(&ai, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = ae_randominteger(2, _state);
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                a1.ptr.p_double[i] = a.ptr.p_double[i];
                a2.ptr.p_double[i] = a.ptr.p_double[i];
                a3.ptr.p_double[i] = a.ptr.p_double[i];
                ar.ptr.p_double[i] = i;
                ai.ptr.p_int[i] = i;
            }
            tagsort(&a0, n, &p1, &p2, _state);
            testtsortunit_testsortresults(&a0, &p1, &p2, &a, n, &waserrors, _state);
            tagsortfasti(&a1, &ai, &bufr1, &bufi1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a1.ptr.p_double[i],a0.ptr.p_double[i]))||ai.ptr.p_int[i]!=p1.ptr.p_int[i];
            }
            tagsortfastr(&a2, &ar, &bufr1, &bufr2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = (waserrors||ae_fp_neq(a2.ptr.p_double[i],a0.ptr.p_double[i]))||ae_fp_neq(ar.ptr.p_double[i],p1.ptr.p_int[i]);
            }
            tagsortfast(&a3, &bufr1, n, _state);
            for(i=0; i<=n-1; i++)
            {
                waserrors = waserrors||ae_fp_neq(a3.ptr.p_double[i],a0.ptr.p_double[i]);
            }
        }
    }
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING TAGSORT\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testtsortunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testtsortunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testtsortunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_int[0] = ae_randominteger(3, _state)-1;
}


static void testtsortunit_testsortresults(/* Real    */ ae_vector* asorted,
     /* Integer */ ae_vector* p1,
     /* Integer */ ae_vector* p2,
     /* Real    */ ae_vector* aoriginal,
     ae_int_t n,
     ae_bool* waserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_vector a2;
    double t;
    ae_vector f;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&f, 0, DT_INT, _state, ae_true);

    ae_vector_set_length(&a2, n-1+1, _state);
    ae_vector_set_length(&f, n-1+1, _state);
    
    /*
     * is set ordered?
     */
    for(i=0; i<=n-2; i++)
    {
        *waserrors = *waserrors||ae_fp_greater(asorted->ptr.p_double[i],asorted->ptr.p_double[i+1]);
    }
    
    /*
     * P1 correctness
     */
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||ae_fp_neq(asorted->ptr.p_double[i],aoriginal->ptr.p_double[p1->ptr.p_int[i]]);
    }
    for(i=0; i<=n-1; i++)
    {
        f.ptr.p_int[i] = 0;
    }
    for(i=0; i<=n-1; i++)
    {
        f.ptr.p_int[p1->ptr.p_int[i]] = f.ptr.p_int[p1->ptr.p_int[i]]+1;
    }
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||f.ptr.p_int[i]!=1;
    }
    
    /*
     * P2 correctness
     */
    for(i=0; i<=n-1; i++)
    {
        a2.ptr.p_double[i] = aoriginal->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        if( p2->ptr.p_int[i]!=i )
        {
            t = a2.ptr.p_double[i];
            a2.ptr.p_double[i] = a2.ptr.p_double[p2->ptr.p_int[i]];
            a2.ptr.p_double[p2->ptr.p_int[i]] = t;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||ae_fp_neq(asorted->ptr.p_double[i],a2.ptr.p_double[i]);
    }
    ae_frame_leave(_state);
}



static void testnearestneighborunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state);
static void testnearestneighborunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state);
static ae_bool testnearestneighborunit_kdtresultsdifferent(/* Real    */ ae_matrix* refxy,
     ae_int_t ntotal,
     /* Real    */ ae_matrix* qx,
     /* Real    */ ae_matrix* qxy,
     /* Integer */ ae_vector* qt,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_state *_state);
static double testnearestneighborunit_vnorm(/* Real    */ ae_vector* x,
     ae_int_t n,
     ae_int_t normtype,
     ae_state *_state);
static void testnearestneighborunit_testkdtuniform(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_int_t normtype,
     ae_bool* kdterrors,
     ae_state *_state);
static void testnearestneighborunit_testkdtreeserialization(ae_bool* err,
     ae_state *_state);





/*************************************************************************
Testing Nearest Neighbor Search
*************************************************************************/
ae_bool testnearestneighbor(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_int_t normtype;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t n;
    ae_int_t smalln;
    ae_int_t largen;
    ae_int_t passcount;
    ae_int_t pass;
    ae_bool waserrors;
    ae_bool kdterrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    kdterrors = ae_false;
    passcount = 2;
    smalln = 256;
    largen = 2048;
    ny = 3;
    
    /*
     *
     */
    testnearestneighborunit_testkdtreeserialization(&kdterrors, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        for(normtype=0; normtype<=2; normtype++)
        {
            for(nx=1; nx<=3; nx++)
            {
                
                /*
                 * Test in hypercube
                 */
                ae_matrix_set_length(&xy, largen, nx+ny, _state);
                for(i=0; i<=largen-1; i++)
                {
                    for(j=0; j<=nx+ny-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = 10*ae_randomreal(_state)-5;
                    }
                }
                for(n=1; n<=10; n++)
                {
                    testnearestneighborunit_testkdtuniform(&xy, n, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
                }
                testnearestneighborunit_testkdtuniform(&xy, largen, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
                
                /*
                 * Test clustered (2*N points, pairs of equal points)
                 */
                ae_matrix_set_length(&xy, 2*smalln, nx+ny, _state);
                for(i=0; i<=smalln-1; i++)
                {
                    for(j=0; j<=nx+ny-1; j++)
                    {
                        xy.ptr.pp_double[2*i+0][j] = 10*ae_randomreal(_state)-5;
                        xy.ptr.pp_double[2*i+1][j] = xy.ptr.pp_double[2*i+0][j];
                    }
                }
                testnearestneighborunit_testkdtuniform(&xy, 2*smalln, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
                
                /*
                 * Test degenerate case: all points are same except for one
                 */
                ae_matrix_set_length(&xy, smalln, nx+ny, _state);
                v = ae_randomreal(_state);
                for(i=0; i<=smalln-2; i++)
                {
                    for(j=0; j<=nx+ny-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = v;
                    }
                }
                for(j=0; j<=nx+ny-1; j++)
                {
                    xy.ptr.pp_double[smalln-1][j] = 10*ae_randomreal(_state)-5;
                }
                testnearestneighborunit_testkdtuniform(&xy, smalln, nx, ae_randominteger(ny+1, _state), normtype, &kdterrors, _state);
            }
        }
    }
    
    /*
     * report
     */
    waserrors = kdterrors;
    if( !silent )
    {
        printf("TESTING NEAREST NEIGHBOR SEARCH\n");
        printf("* KD TREES:                              ");
        if( !kdterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testnearestneighborunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testnearestneighborunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Compare results from different queries:
* X     just X-values
* XY    X-values and Y-values
* XT    X-values and tag values
*************************************************************************/
static ae_bool testnearestneighborunit_kdtresultsdifferent(/* Real    */ ae_matrix* refxy,
     ae_int_t ntotal,
     /* Real    */ ae_matrix* qx,
     /* Real    */ ae_matrix* qxy,
     /* Integer */ ae_vector* qt,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_false;
    for(i=0; i<=n-1; i++)
    {
        if( qt->ptr.p_int[i]<0||qt->ptr.p_int[i]>=ntotal )
        {
            result = ae_true;
            return result;
        }
        for(j=0; j<=nx-1; j++)
        {
            result = result||ae_fp_neq(qx->ptr.pp_double[i][j],refxy->ptr.pp_double[qt->ptr.p_int[i]][j]);
            result = result||ae_fp_neq(qxy->ptr.pp_double[i][j],refxy->ptr.pp_double[qt->ptr.p_int[i]][j]);
        }
        for(j=0; j<=ny-1; j++)
        {
            result = result||ae_fp_neq(qxy->ptr.pp_double[i][nx+j],refxy->ptr.pp_double[qt->ptr.p_int[i]][nx+j]);
        }
    }
    return result;
}


/*************************************************************************
Returns norm
*************************************************************************/
static double testnearestneighborunit_vnorm(/* Real    */ ae_vector* x,
     ae_int_t n,
     ae_int_t normtype,
     ae_state *_state)
{
    ae_int_t i;
    double result;


    result = ae_randomreal(_state);
    if( normtype==0 )
    {
        result = 0;
        for(i=0; i<=n-1; i++)
        {
            result = ae_maxreal(result, ae_fabs(x->ptr.p_double[i], _state), _state);
        }
        return result;
    }
    if( normtype==1 )
    {
        result = 0;
        for(i=0; i<=n-1; i++)
        {
            result = result+ae_fabs(x->ptr.p_double[i], _state);
        }
        return result;
    }
    if( normtype==2 )
    {
        result = 0;
        for(i=0; i<=n-1; i++)
        {
            result = result+ae_sqr(x->ptr.p_double[i], _state);
        }
        result = ae_sqrt(result, _state);
        return result;
    }
    return result;
}


/*************************************************************************
Testing Nearest Neighbor Search on uniformly distributed hypercube

NormType: 0, 1, 2
D: space dimension
N: points count
*************************************************************************/
static void testnearestneighborunit_testkdtuniform(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t ny,
     ae_int_t normtype,
     ae_bool* kdterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double errtol;
    ae_vector tags;
    ae_vector ptx;
    ae_vector tmpx;
    ae_vector tmpb;
    kdtree treex;
    kdtree treexy;
    kdtree treext;
    ae_matrix qx;
    ae_matrix qxy;
    ae_vector qtags;
    ae_vector qr;
    ae_int_t kx;
    ae_int_t kxy;
    ae_int_t kt;
    double eps;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t task;
    ae_bool isequal;
    double r;
    ae_int_t q;
    ae_int_t qcount;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
    ae_vector_init(&ptx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpb, 0, DT_BOOL, _state, ae_true);
    _kdtree_init(&treex, _state, ae_true);
    _kdtree_init(&treexy, _state, ae_true);
    _kdtree_init(&treext, _state, ae_true);
    ae_matrix_init(&qx, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&qxy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qtags, 0, DT_INT, _state, ae_true);
    ae_vector_init(&qr, 0, DT_REAL, _state, ae_true);

    qcount = 10;
    
    /*
     * Tol - roundoff error tolerance (for '>=' comparisons)
     */
    errtol = 100000*ae_machineepsilon;
    
    /*
     * fill tags
     */
    ae_vector_set_length(&tags, n, _state);
    for(i=0; i<=n-1; i++)
    {
        tags.ptr.p_int[i] = i;
    }
    
    /*
     * build trees
     */
    kdtreebuild(xy, n, nx, 0, normtype, &treex, _state);
    kdtreebuild(xy, n, nx, ny, normtype, &treexy, _state);
    kdtreebuildtagged(xy, &tags, n, nx, 0, normtype, &treext, _state);
    
    /*
     * allocate arrays
     */
    ae_vector_set_length(&tmpx, nx, _state);
    ae_vector_set_length(&tmpb, n, _state);
    ae_matrix_set_length(&qx, n, nx, _state);
    ae_matrix_set_length(&qxy, n, nx+ny, _state);
    ae_vector_set_length(&qtags, n, _state);
    ae_vector_set_length(&qr, n, _state);
    ae_vector_set_length(&ptx, nx, _state);
    
    /*
     * test general K-NN queries (with self-matches):
     * * compare results from different trees (must be equal) and
     *   check that correct (value,tag) pairs are returned
     * * test results from XT tree - let R be radius of query result.
     *   then all points not in result must be not closer than R.
     */
    for(q=1; q<=qcount; q++)
    {
        
        /*
         * Select K: 1..N
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            k = 1+ae_randominteger(n, _state);
        }
        else
        {
            k = 1;
        }
        
        /*
         * Select point (either one of the points, or random)
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            i = ae_randominteger(n, _state);
            ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        }
        else
        {
            for(i=0; i<=nx-1; i++)
            {
                ptx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * Test:
         * * consistency of results from different queries
         * * points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * distances are correct and are ordered
         */
        kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
        kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
        kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
        if( (kx!=k||kxy!=k)||kt!=k )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        kdtreequeryresultsxi(&treex, &qx, _state);
        kdtreequeryresultsxyi(&treexy, &qxy, _state);
        kdtreequeryresultstagsi(&treext, &qtags, _state);
        kdtreequeryresultsdistancesi(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        kdtreequeryresultsx(&treex, &qx, _state);
        kdtreequeryresultsxy(&treexy, &qxy, _state);
        kdtreequeryresultstags(&treext, &qtags, _state);
        kdtreequeryresultsdistances(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        r = 0;
        for(i=0; i<=k-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &qx.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            r = ae_maxreal(r, testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state), _state);
        }
        for(i=0; i<=n-1; i++)
        {
            if( tmpb.ptr.p_bool[i] )
            {
                ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
                ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol));
            }
        }
        for(i=0; i<=k-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
        for(i=0; i<=k-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[qtags.ptr.p_int[i]][0], 1, ae_v_len(0,nx-1));
            *kdterrors = *kdterrors||ae_fp_greater(ae_fabs(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state)-qr.ptr.p_double[i], _state),errtol);
        }
        
        /*
         * Test reallocation properties: buffered functions must automatically
         * resize array which is too small, but leave unchanged array which is
         * too large.
         */
        if( n>=2 )
        {
            
            /*
             * First step: array is too small, two elements are required
             */
            k = 2;
            kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
            kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
            kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
            if( (kx!=k||kxy!=k)||kt!=k )
            {
                *kdterrors = ae_true;
                ae_frame_leave(_state);
                return;
            }
            ae_matrix_set_length(&qx, 1, 1, _state);
            ae_matrix_set_length(&qxy, 1, 1, _state);
            ae_vector_set_length(&qtags, 1, _state);
            ae_vector_set_length(&qr, 1, _state);
            kdtreequeryresultsx(&treex, &qx, _state);
            kdtreequeryresultsxy(&treexy, &qxy, _state);
            kdtreequeryresultstags(&treext, &qtags, _state);
            kdtreequeryresultsdistances(&treext, &qr, _state);
            *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
            
            /*
             * Second step: array is one row larger than needed, so only first
             * row is overwritten. Test it.
             */
            k = 1;
            kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
            kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
            kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
            if( (kx!=k||kxy!=k)||kt!=k )
            {
                *kdterrors = ae_true;
                ae_frame_leave(_state);
                return;
            }
            for(i=0; i<=nx-1; i++)
            {
                qx.ptr.pp_double[1][i] = _state->v_nan;
            }
            for(i=0; i<=nx+ny-1; i++)
            {
                qxy.ptr.pp_double[1][i] = _state->v_nan;
            }
            qtags.ptr.p_int[1] = 999;
            qr.ptr.p_double[1] = _state->v_nan;
            kdtreequeryresultsx(&treex, &qx, _state);
            kdtreequeryresultsxy(&treexy, &qxy, _state);
            kdtreequeryresultstags(&treext, &qtags, _state);
            kdtreequeryresultsdistances(&treext, &qr, _state);
            *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
            for(i=0; i<=nx-1; i++)
            {
                *kdterrors = *kdterrors||!ae_isnan(qx.ptr.pp_double[1][i], _state);
            }
            for(i=0; i<=nx+ny-1; i++)
            {
                *kdterrors = *kdterrors||!ae_isnan(qxy.ptr.pp_double[1][i], _state);
            }
            *kdterrors = *kdterrors||!(qtags.ptr.p_int[1]==999);
            *kdterrors = *kdterrors||!ae_isnan(qr.ptr.p_double[1], _state);
        }
        
        /*
         * Test reallocation properties: 'interactive' functions must allocate
         * new array on each call.
         */
        if( n>=2 )
        {
            
            /*
             * On input array is either too small or too large
             */
            for(k=1; k<=2; k++)
            {
                ae_assert(k==1||k==2, "KNN: internal error (unexpected K)!", _state);
                kx = kdtreequeryknn(&treex, &ptx, k, ae_true, _state);
                kxy = kdtreequeryknn(&treexy, &ptx, k, ae_true, _state);
                kt = kdtreequeryknn(&treext, &ptx, k, ae_true, _state);
                if( (kx!=k||kxy!=k)||kt!=k )
                {
                    *kdterrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ae_matrix_set_length(&qx, 3-k, 3-k, _state);
                ae_matrix_set_length(&qxy, 3-k, 3-k, _state);
                ae_vector_set_length(&qtags, 3-k, _state);
                ae_vector_set_length(&qr, 3-k, _state);
                kdtreequeryresultsxi(&treex, &qx, _state);
                kdtreequeryresultsxyi(&treexy, &qxy, _state);
                kdtreequeryresultstagsi(&treext, &qtags, _state);
                kdtreequeryresultsdistancesi(&treext, &qr, _state);
                *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
                *kdterrors = (*kdterrors||qx.rows!=k)||qx.cols!=nx;
                *kdterrors = (*kdterrors||qxy.rows!=k)||qxy.cols!=nx+ny;
                *kdterrors = *kdterrors||qtags.cnt!=k;
                *kdterrors = *kdterrors||qr.cnt!=k;
            }
        }
    }
    
    /*
     * test general approximate K-NN queries (with self-matches):
     * * compare results from different trees (must be equal) and
     *   check that correct (value,tag) pairs are returned
     * * test results from XT tree - let R be radius of query result.
     *   then all points not in result must be not closer than R/(1+Eps).
     */
    for(q=1; q<=qcount; q++)
    {
        
        /*
         * Select K: 1..N
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            k = 1+ae_randominteger(n, _state);
        }
        else
        {
            k = 1;
        }
        
        /*
         * Select Eps
         */
        eps = 0.5+ae_randomreal(_state);
        
        /*
         * Select point (either one of the points, or random)
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            i = ae_randominteger(n, _state);
            ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        }
        else
        {
            for(i=0; i<=nx-1; i++)
            {
                ptx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * Test:
         * * consistency of results from different queries
         * * points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * distances are correct and are ordered
         */
        kx = kdtreequeryaknn(&treex, &ptx, k, ae_true, eps, _state);
        kxy = kdtreequeryaknn(&treexy, &ptx, k, ae_true, eps, _state);
        kt = kdtreequeryaknn(&treext, &ptx, k, ae_true, eps, _state);
        if( (kx!=k||kxy!=k)||kt!=k )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        kdtreequeryresultsxi(&treex, &qx, _state);
        kdtreequeryresultsxyi(&treexy, &qxy, _state);
        kdtreequeryresultstagsi(&treext, &qtags, _state);
        kdtreequeryresultsdistancesi(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        kdtreequeryresultsx(&treex, &qx, _state);
        kdtreequeryresultsxy(&treexy, &qxy, _state);
        kdtreequeryresultstags(&treext, &qtags, _state);
        kdtreequeryresultsdistances(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, k, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        r = 0;
        for(i=0; i<=k-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &qx.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            r = ae_maxreal(r, testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state), _state);
        }
        for(i=0; i<=n-1; i++)
        {
            if( tmpb.ptr.p_bool[i] )
            {
                ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
                ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol)/(1+eps));
            }
        }
        for(i=0; i<=k-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
        for(i=0; i<=k-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[qtags.ptr.p_int[i]][0], 1, ae_v_len(0,nx-1));
            *kdterrors = *kdterrors||ae_fp_greater(ae_fabs(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state)-qr.ptr.p_double[i], _state),errtol);
        }
    }
    
    /*
     * test general R-NN queries  (with self-matches):
     * * compare results from different trees (must be equal) and
     *   check that correct (value,tag) pairs are returned
     * * test results from XT tree - let R be radius of query result.
     *   then all points not in result must be not closer than R.
     */
    for(q=1; q<=qcount; q++)
    {
        
        /*
         * Select R
         */
        if( ae_fp_greater(ae_randomreal(_state),0.3) )
        {
            r = ae_maxreal(ae_randomreal(_state), ae_machineepsilon, _state);
        }
        else
        {
            r = ae_machineepsilon;
        }
        
        /*
         * Select point (either one of the points, or random)
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            i = ae_randominteger(n, _state);
            ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        }
        else
        {
            for(i=0; i<=nx-1; i++)
            {
                ptx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * Test:
         * * consistency of results from different queries
         * * points in query are IN the R-sphere (or at the boundary),
         *   and points not in query are outside of the R-sphere (or at the boundary)
         * * distances are correct and are ordered
         */
        kx = kdtreequeryrnn(&treex, &ptx, r, ae_true, _state);
        kxy = kdtreequeryrnn(&treexy, &ptx, r, ae_true, _state);
        kt = kdtreequeryrnn(&treext, &ptx, r, ae_true, _state);
        if( kxy!=kx||kt!=kx )
        {
            *kdterrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        kdtreequeryresultsxi(&treex, &qx, _state);
        kdtreequeryresultsxyi(&treexy, &qxy, _state);
        kdtreequeryresultstagsi(&treext, &qtags, _state);
        kdtreequeryresultsdistancesi(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, kx, nx, ny, _state);
        kdtreequeryresultsx(&treex, &qx, _state);
        kdtreequeryresultsxy(&treexy, &qxy, _state);
        kdtreequeryresultstags(&treext, &qtags, _state);
        kdtreequeryresultsdistances(&treext, &qr, _state);
        *kdterrors = *kdterrors||testnearestneighborunit_kdtresultsdifferent(xy, n, &qx, &qxy, &qtags, kx, nx, ny, _state);
        for(i=0; i<=n-1; i++)
        {
            tmpb.ptr.p_bool[i] = ae_true;
        }
        for(i=0; i<=kx-1; i++)
        {
            tmpb.ptr.p_bool[qtags.ptr.p_int[i]] = ae_false;
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_move(&tmpx.ptr.p_double[0], 1, &ptx.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            ae_v_sub(&tmpx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            if( tmpb.ptr.p_bool[i] )
            {
                *kdterrors = *kdterrors||ae_fp_less(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1-errtol));
            }
            else
            {
                *kdterrors = *kdterrors||ae_fp_greater(testnearestneighborunit_vnorm(&tmpx, nx, normtype, _state),r*(1+errtol));
            }
        }
        for(i=0; i<=kx-2; i++)
        {
            *kdterrors = *kdterrors||ae_fp_greater(qr.ptr.p_double[i],qr.ptr.p_double[i+1]);
        }
    }
    
    /*
     * Test self-matching:
     * * self-match - nearest neighbor of each point in XY is the point itself
     * * no self-match - nearest neighbor is NOT the point itself
     */
    if( n>1 )
    {
        
        /*
         * test for N=1 have non-general form, but it is not really needed
         */
        for(task=0; task<=1; task++)
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&ptx.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                kx = kdtreequeryknn(&treex, &ptx, 1, task==0, _state);
                kdtreequeryresultsxi(&treex, &qx, _state);
                if( kx!=1 )
                {
                    *kdterrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                isequal = ae_true;
                for(j=0; j<=nx-1; j++)
                {
                    isequal = isequal&&ae_fp_eq(qx.ptr.pp_double[0][j],ptx.ptr.p_double[j]);
                }
                if( task==0 )
                {
                    *kdterrors = *kdterrors||!isequal;
                }
                else
                {
                    *kdterrors = *kdterrors||isequal;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Testing serialization of KD trees

This function sets Err to True on errors, but leaves it unchanged on success
*************************************************************************/
static void testnearestneighborunit_testkdtreeserialization(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t nx;
    ae_int_t ny;
    ae_int_t normtype;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t q;
    ae_matrix xy;
    ae_vector x;
    ae_vector tags;
    ae_vector qsizes;
    double threshold;
    kdtree tree0;
    kdtree tree1;
    ae_int_t k0;
    ae_int_t k1;
    ae_matrix xy0;
    ae_matrix xy1;
    ae_vector tags0;
    ae_vector tags1;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
    ae_vector_init(&qsizes, 0, DT_INT, _state, ae_true);
    _kdtree_init(&tree0, _state, ae_true);
    _kdtree_init(&tree1, _state, ae_true);
    ae_matrix_init(&xy0, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy1, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tags0, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tags1, 0, DT_INT, _state, ae_true);

    threshold = 100*ae_machineepsilon;
    
    /*
     * different N, NX, NY, NormType
     */
    n = 1;
    while(n<=51)
    {
        
        /*
         * prepare array with query sizes
         */
        ae_vector_set_length(&qsizes, 4, _state);
        qsizes.ptr.p_int[0] = 1;
        qsizes.ptr.p_int[1] = ae_minint(2, n, _state);
        qsizes.ptr.p_int[2] = ae_minint(4, n, _state);
        qsizes.ptr.p_int[3] = n;
        
        /*
         * different NX/NY/NormType
         */
        for(nx=1; nx<=2; nx++)
        {
            for(ny=0; ny<=2; ny++)
            {
                for(normtype=0; normtype<=2; normtype++)
                {
                    
                    /*
                     * Prepare data
                     */
                    ae_matrix_set_length(&xy, n, nx+ny, _state);
                    ae_vector_set_length(&tags, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=nx+ny-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                        }
                        tags.ptr.p_int[i] = ae_randominteger(100, _state);
                    }
                    
                    /*
                     * Build tree, pass it through serializer
                     */
                    kdtreebuildtagged(&xy, &tags, n, nx, ny, normtype, &tree0, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        kdtreealloc(&_local_serializer, &tree0, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        kdtreeserialize(&_local_serializer, &tree0, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        kdtreeunserialize(&_local_serializer, &tree1, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                    
                    /*
                     * For each point of XY we make queries with different sizes
                     */
                    ae_vector_set_length(&x, nx, _state);
                    for(k=0; k<=n-1; k++)
                    {
                        for(q=0; q<=qsizes.cnt-1; q++)
                        {
                            ae_v_move(&x.ptr.p_double[0], 1, &xy.ptr.pp_double[k][0], 1, ae_v_len(0,nx-1));
                            k0 = kdtreequeryknn(&tree0, &x, qsizes.ptr.p_int[q], ae_true, _state);
                            k1 = kdtreequeryknn(&tree1, &x, qsizes.ptr.p_int[q], ae_true, _state);
                            if( k0!=k1 )
                            {
                                *err = ae_true;
                                ae_frame_leave(_state);
                                return;
                            }
                            kdtreequeryresultsxy(&tree0, &xy0, _state);
                            kdtreequeryresultsxy(&tree1, &xy1, _state);
                            for(i=0; i<=k0-1; i++)
                            {
                                for(j=0; j<=nx+ny-1; j++)
                                {
                                    if( ae_fp_greater(ae_fabs(xy0.ptr.pp_double[i][j]-xy1.ptr.pp_double[i][j], _state),threshold) )
                                    {
                                        *err = ae_true;
                                        ae_frame_leave(_state);
                                        return;
                                    }
                                }
                            }
                            kdtreequeryresultstags(&tree0, &tags0, _state);
                            kdtreequeryresultstags(&tree1, &tags1, _state);
                            for(i=0; i<=k0-1; i++)
                            {
                                if( tags0.ptr.p_int[i]!=tags1.ptr.p_int[i] )
                                {
                                    *err = ae_true;
                                    ae_frame_leave(_state);
                                    return;
                                }
                            }
                        }
                    }
                }
            }
        }
        
        /*
         * Next N
         */
        n = n+25;
    }
    ae_frame_leave(_state);
}



static void testablasunit_naivematrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     double alpha,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     double beta,
     ae_state *_state);
static ae_bool testablasunit_testtrsm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testsyrk(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testgemm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testtrans(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testrank1(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testmv(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static ae_bool testablasunit_testcopy(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state);
static void testablasunit_refcmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static void testablasunit_refcmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static void testablasunit_refrmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static void testablasunit_refrmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state);
static ae_bool testablasunit_internalcmatrixtrinverse(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testablasunit_internalrmatrixtrinverse(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static void testablasunit_refcmatrixsyrk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state);
static void testablasunit_refrmatrixsyrk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state);
static void testablasunit_refcmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     ae_complex alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Complex */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     ae_complex beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state);
static void testablasunit_refrmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Real    */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state);





ae_bool testablas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_bool trsmerrors;
    ae_bool syrkerrors;
    ae_bool gemmerrors;
    ae_bool transerrors;
    ae_bool rank1errors;
    ae_bool mverrors;
    ae_bool copyerrors;
    ae_bool waserrors;
    ae_matrix ra;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);

    trsmerrors = ae_false;
    syrkerrors = ae_false;
    gemmerrors = ae_false;
    transerrors = ae_false;
    rank1errors = ae_false;
    mverrors = ae_false;
    copyerrors = ae_false;
    waserrors = ae_false;
    threshold = 10000*ae_machineepsilon;
    trsmerrors = trsmerrors||testablasunit_testtrsm(1, 3*ablasblocksize(&ra, _state)+1, _state);
    syrkerrors = syrkerrors||testablasunit_testsyrk(1, 3*ablasblocksize(&ra, _state)+1, _state);
    gemmerrors = gemmerrors||testablasunit_testgemm(1, 3*ablasblocksize(&ra, _state)+1, _state);
    transerrors = transerrors||testablasunit_testtrans(1, 3*ablasblocksize(&ra, _state)+1, _state);
    rank1errors = rank1errors||testablasunit_testrank1(1, 3*ablasblocksize(&ra, _state)+1, _state);
    mverrors = mverrors||testablasunit_testmv(1, 3*ablasblocksize(&ra, _state)+1, _state);
    copyerrors = copyerrors||testablasunit_testcopy(1, 3*ablasblocksize(&ra, _state)+1, _state);
    
    /*
     * report
     */
    waserrors = (((((trsmerrors||syrkerrors)||gemmerrors)||transerrors)||rank1errors)||mverrors)||copyerrors;
    if( !silent )
    {
        printf("TESTING ABLAS\n");
        printf("* TRSM:                                  ");
        if( trsmerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SYRK:                                  ");
        if( syrkerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* GEMM:                                  ");
        if( gemmerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* TRANS:                                 ");
        if( transerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* RANK1:                                 ");
        if( rank1errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* MV:                                    ");
        if( mverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COPY:                                  ");
        if( copyerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testablasunit_naivematrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     double alpha,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     double beta,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t arows;
    ae_int_t acols;
    ae_int_t brows;
    ae_int_t bcols;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t r;
    double v;
    ae_vector x1;
    ae_vector x2;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Setup
     */
    if( !transa )
    {
        arows = ai2-ai1+1;
        acols = aj2-aj1+1;
    }
    else
    {
        arows = aj2-aj1+1;
        acols = ai2-ai1+1;
    }
    if( !transb )
    {
        brows = bi2-bi1+1;
        bcols = bj2-bj1+1;
    }
    else
    {
        brows = bj2-bj1+1;
        bcols = bi2-bi1+1;
    }
    ae_assert(acols==brows, "NaiveMatrixMatrixMultiply: incorrect matrix sizes!", _state);
    if( ((arows<=0||acols<=0)||brows<=0)||bcols<=0 )
    {
        ae_frame_leave(_state);
        return;
    }
    l = arows;
    r = bcols;
    k = acols;
    ae_vector_set_length(&x1, k+1, _state);
    ae_vector_set_length(&x2, k+1, _state);
    for(i=1; i<=l; i++)
    {
        for(j=1; j<=r; j++)
        {
            if( !transa )
            {
                if( !transb )
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1][bj1+j-1], b->stride, &a->ptr.pp_double[ai1+i-1][aj1], 1, ae_v_len(bi1,bi2));
                }
                else
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1+j-1][bj1], 1, &a->ptr.pp_double[ai1+i-1][aj1], 1, ae_v_len(bj1,bj2));
                }
            }
            else
            {
                if( !transb )
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1][bj1+j-1], b->stride, &a->ptr.pp_double[ai1][aj1+i-1], a->stride, ae_v_len(bi1,bi2));
                }
                else
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1+j-1][bj1], 1, &a->ptr.pp_double[ai1][aj1+i-1], a->stride, ae_v_len(bj1,bj2));
                }
            }
            if( ae_fp_eq(beta,0) )
            {
                c->ptr.pp_double[ci1+i-1][cj1+j-1] = alpha*v;
            }
            else
            {
                c->ptr.pp_double[ci1+i-1][cj1+j-1] = beta*c->ptr.pp_double[ci1+i-1][cj1+j-1]+alpha*v;
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
?Matrix????TRSM tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testtrsm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t optype;
    ae_int_t uppertype;
    ae_int_t unittype;
    ae_int_t xoffsi;
    ae_int_t xoffsj;
    ae_int_t aoffsitype;
    ae_int_t aoffsjtype;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_matrix refra;
    ae_matrix refrxl;
    ae_matrix refrxr;
    ae_matrix refca;
    ae_matrix refcxl;
    ae_matrix refcxr;
    ae_matrix ra;
    ae_matrix ca;
    ae_matrix rxr1;
    ae_matrix rxl1;
    ae_matrix cxr1;
    ae_matrix cxl1;
    ae_matrix rxr2;
    ae_matrix rxl2;
    ae_matrix cxr2;
    ae_matrix cxl2;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrxl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrxr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcxl, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcxr, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rxr1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rxl1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cxr1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cxl1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rxr2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rxl2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cxr2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cxl2, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = ae_sqr(maxn, _state)*100*ae_machineepsilon;
    result = ae_false;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize RefRA/RefCA by random matrices whose upper
         * and lower triangle submatrices are non-degenerate
         * well-conditioned matrices.
         *
         * Matrix size is 2Mx2M (four copies of same MxM matrix
         * to test different offsets)
         */
        ae_matrix_set_length(&refra, 2*m, 2*m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refra.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
            }
        }
        for(i=0; i<=m-1; i++)
        {
            refra.ptr.pp_double[i][i] = (2*ae_randominteger(1, _state)-1)*(2*m+ae_randomreal(_state));
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refra.ptr.pp_double[i+m][j] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i][j+m] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i+m][j+m] = refra.ptr.pp_double[i][j];
            }
        }
        ae_matrix_set_length(&refca, 2*m, 2*m, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refca.ptr.pp_complex[i][j].x = 0.2*ae_randomreal(_state)-0.1;
                refca.ptr.pp_complex[i][j].y = 0.2*ae_randomreal(_state)-0.1;
            }
        }
        for(i=0; i<=m-1; i++)
        {
            refca.ptr.pp_complex[i][i].x = (2*ae_randominteger(2, _state)-1)*(2*m+ae_randomreal(_state));
            refca.ptr.pp_complex[i][i].y = (2*ae_randominteger(2, _state)-1)*(2*m+ae_randomreal(_state));
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refca.ptr.pp_complex[i+m][j] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i][j+m] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i+m][j+m] = refca.ptr.pp_complex[i][j];
            }
        }
        
        /*
         * Generate random XL/XR.
         *
         * XR is NxM matrix (matrix for 'Right' subroutines)
         * XL is MxN matrix (matrix for 'Left' subroutines)
         */
        ae_matrix_set_length(&refrxr, n, m, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refrxr.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        ae_matrix_set_length(&refrxl, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                refrxl.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        ae_matrix_set_length(&refcxr, n, m, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                refcxr.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcxr.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        ae_matrix_set_length(&refcxl, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                refcxl.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcxl.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * test different types of operations, offsets, and so on...
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        ae_matrix_set_length(&ra, 2*m, 2*m, _state);
        ae_matrix_set_length(&rxr1, n, m, _state);
        ae_matrix_set_length(&rxr2, n, m, _state);
        ae_matrix_set_length(&rxl1, m, n, _state);
        ae_matrix_set_length(&rxl2, m, n, _state);
        ae_matrix_set_length(&ca, 2*m, 2*m, _state);
        ae_matrix_set_length(&cxr1, n, m, _state);
        ae_matrix_set_length(&cxr2, n, m, _state);
        ae_matrix_set_length(&cxl1, m, n, _state);
        ae_matrix_set_length(&cxl2, m, n, _state);
        optype = ae_randominteger(3, _state);
        uppertype = ae_randominteger(2, _state);
        unittype = ae_randominteger(2, _state);
        xoffsi = ae_randominteger(2, _state);
        xoffsj = ae_randominteger(2, _state);
        aoffsitype = ae_randominteger(2, _state);
        aoffsjtype = ae_randominteger(2, _state);
        aoffsi = m*aoffsitype;
        aoffsj = m*aoffsjtype;
        
        /*
         * copy A, XR, XL (fill unused parts with random garbage)
         */
        for(i=0; i<=2*m-1; i++)
        {
            for(j=0; j<=2*m-1; j++)
            {
                if( ((i>=aoffsi&&i<aoffsi+m)&&j>=aoffsj)&&j<aoffsj+m )
                {
                    ca.ptr.pp_complex[i][j] = refca.ptr.pp_complex[i][j];
                    ra.ptr.pp_double[i][j] = refra.ptr.pp_double[i][j];
                }
                else
                {
                    ca.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    ra.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                if( i>=xoffsi&&j>=xoffsj )
                {
                    cxr1.ptr.pp_complex[i][j] = refcxr.ptr.pp_complex[i][j];
                    cxr2.ptr.pp_complex[i][j] = refcxr.ptr.pp_complex[i][j];
                    rxr1.ptr.pp_double[i][j] = refrxr.ptr.pp_double[i][j];
                    rxr2.ptr.pp_double[i][j] = refrxr.ptr.pp_double[i][j];
                }
                else
                {
                    cxr1.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    cxr2.ptr.pp_complex[i][j] = cxr1.ptr.pp_complex[i][j];
                    rxr1.ptr.pp_double[i][j] = ae_randomreal(_state);
                    rxr2.ptr.pp_double[i][j] = rxr1.ptr.pp_double[i][j];
                }
            }
        }
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i>=xoffsi&&j>=xoffsj )
                {
                    cxl1.ptr.pp_complex[i][j] = refcxl.ptr.pp_complex[i][j];
                    cxl2.ptr.pp_complex[i][j] = refcxl.ptr.pp_complex[i][j];
                    rxl1.ptr.pp_double[i][j] = refrxl.ptr.pp_double[i][j];
                    rxl2.ptr.pp_double[i][j] = refrxl.ptr.pp_double[i][j];
                }
                else
                {
                    cxl1.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    cxl2.ptr.pp_complex[i][j] = cxl1.ptr.pp_complex[i][j];
                    rxl1.ptr.pp_double[i][j] = ae_randomreal(_state);
                    rxl2.ptr.pp_double[i][j] = rxl1.ptr.pp_double[i][j];
                }
            }
        }
        
        /*
         * Test CXR
         */
        cmatrixrighttrsm(n-xoffsi, m-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxr1, xoffsi, xoffsj, _state);
        testablasunit_refcmatrixrighttrsm(n-xoffsi, m-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxr2, xoffsi, xoffsj, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cxr1.ptr.pp_complex[i][j],cxr2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        
        /*
         * Test CXL
         */
        cmatrixlefttrsm(m-xoffsi, n-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxl1, xoffsi, xoffsj, _state);
        testablasunit_refcmatrixlefttrsm(m-xoffsi, n-xoffsj, &ca, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &cxl2, xoffsi, xoffsj, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cxl1.ptr.pp_complex[i][j],cxl2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        if( optype<2 )
        {
            
            /*
             * Test RXR
             */
            rmatrixrighttrsm(n-xoffsi, m-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxr1, xoffsi, xoffsj, _state);
            testablasunit_refrmatrixrighttrsm(n-xoffsi, m-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxr2, xoffsi, xoffsj, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    result = result||ae_fp_greater(ae_fabs(rxr1.ptr.pp_double[i][j]-rxr2.ptr.pp_double[i][j], _state),threshold);
                }
            }
            
            /*
             * Test RXL
             */
            rmatrixlefttrsm(m-xoffsi, n-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxl1, xoffsi, xoffsj, _state);
            testablasunit_refrmatrixlefttrsm(m-xoffsi, n-xoffsj, &ra, aoffsi, aoffsj, uppertype==0, unittype==0, optype, &rxl2, xoffsi, xoffsj, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = result||ae_fp_greater(ae_fabs(rxl1.ptr.pp_double[i][j]-rxl2.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
SYRK tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testsyrk(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t k;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t uppertype;
    ae_int_t xoffsi;
    ae_int_t xoffsj;
    ae_int_t aoffsitype;
    ae_int_t aoffsjtype;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t alphatype;
    ae_int_t betatype;
    ae_matrix refra;
    ae_matrix refrc;
    ae_matrix refca;
    ae_matrix refcc;
    double alpha;
    double beta;
    ae_matrix ra1;
    ae_matrix ra2;
    ae_matrix ca1;
    ae_matrix ca2;
    ae_matrix rc;
    ae_matrix rct;
    ae_matrix cc;
    ae_matrix cct;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcc, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ra1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ra2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ca2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rct, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cc, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cct, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = maxn*100*ae_machineepsilon;
    result = ae_false;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        k = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            k = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize RefRA/RefCA by random Hermitian matrices,
         * RefRC/RefCC by random matrices
         *
         * RA/CA size is 2Nx2N (four copies of same NxN matrix
         * to test different offsets)
         */
        ae_matrix_set_length(&refra, 2*n, 2*n, _state);
        ae_matrix_set_length(&refca, 2*n, 2*n, _state);
        for(i=0; i<=n-1; i++)
        {
            refra.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
            refca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            for(j=i+1; j<=n-1; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refra.ptr.pp_double[j][i] = refra.ptr.pp_double[i][j];
                refca.ptr.pp_complex[j][i] = ae_c_conj(refca.ptr.pp_complex[i][j], _state);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                refra.ptr.pp_double[i+n][j] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i][j+n] = refra.ptr.pp_double[i][j];
                refra.ptr.pp_double[i+n][j+n] = refra.ptr.pp_double[i][j];
                refca.ptr.pp_complex[i+n][j] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i][j+n] = refca.ptr.pp_complex[i][j];
                refca.ptr.pp_complex[i+n][j+n] = refca.ptr.pp_complex[i][j];
            }
        }
        ae_matrix_set_length(&refrc, n, k, _state);
        ae_matrix_set_length(&refcc, n, k, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=k-1; j++)
            {
                refrc.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * test different types of operations, offsets, and so on...
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        ae_matrix_set_length(&ra1, 2*n, 2*n, _state);
        ae_matrix_set_length(&ra2, 2*n, 2*n, _state);
        ae_matrix_set_length(&ca1, 2*n, 2*n, _state);
        ae_matrix_set_length(&ca2, 2*n, 2*n, _state);
        ae_matrix_set_length(&rc, n, k, _state);
        ae_matrix_set_length(&rct, k, n, _state);
        ae_matrix_set_length(&cc, n, k, _state);
        ae_matrix_set_length(&cct, k, n, _state);
        uppertype = ae_randominteger(2, _state);
        xoffsi = ae_randominteger(2, _state);
        xoffsj = ae_randominteger(2, _state);
        aoffsitype = ae_randominteger(2, _state);
        aoffsjtype = ae_randominteger(2, _state);
        alphatype = ae_randominteger(2, _state);
        betatype = ae_randominteger(2, _state);
        aoffsi = n*aoffsitype;
        aoffsj = n*aoffsjtype;
        alpha = alphatype*(2*ae_randomreal(_state)-1);
        beta = betatype*(2*ae_randomreal(_state)-1);
        
        /*
         * copy A, C (fill unused parts with random garbage)
         */
        for(i=0; i<=2*n-1; i++)
        {
            for(j=0; j<=2*n-1; j++)
            {
                if( ((i>=aoffsi&&i<aoffsi+n)&&j>=aoffsj)&&j<aoffsj+n )
                {
                    ca1.ptr.pp_complex[i][j] = refca.ptr.pp_complex[i][j];
                    ca2.ptr.pp_complex[i][j] = refca.ptr.pp_complex[i][j];
                    ra1.ptr.pp_double[i][j] = refra.ptr.pp_double[i][j];
                    ra2.ptr.pp_double[i][j] = refra.ptr.pp_double[i][j];
                }
                else
                {
                    ca1.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    ca2.ptr.pp_complex[i][j] = ca1.ptr.pp_complex[i][j];
                    ra1.ptr.pp_double[i][j] = ae_randomreal(_state);
                    ra2.ptr.pp_double[i][j] = ra1.ptr.pp_double[i][j];
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=k-1; j++)
            {
                if( i>=xoffsi&&j>=xoffsj )
                {
                    rc.ptr.pp_double[i][j] = refrc.ptr.pp_double[i][j];
                    rct.ptr.pp_double[j][i] = refrc.ptr.pp_double[i][j];
                    cc.ptr.pp_complex[i][j] = refcc.ptr.pp_complex[i][j];
                    cct.ptr.pp_complex[j][i] = refcc.ptr.pp_complex[i][j];
                }
                else
                {
                    rc.ptr.pp_double[i][j] = ae_randomreal(_state);
                    rct.ptr.pp_double[j][i] = rc.ptr.pp_double[i][j];
                    cc.ptr.pp_complex[i][j] = ae_complex_from_d(ae_randomreal(_state));
                    cct.ptr.pp_complex[j][i] = cct.ptr.pp_complex[j][i];
                }
            }
        }
        
        /*
         * Test complex
         * Only one of transform types is selected and tested
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            cmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &cc, xoffsi, xoffsj, 0, beta, &ca1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refcmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &cc, xoffsi, xoffsj, 0, beta, &ca2, aoffsi, aoffsj, uppertype==0, _state);
        }
        else
        {
            cmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &cct, xoffsj, xoffsi, 2, beta, &ca1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refcmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &cct, xoffsj, xoffsi, 2, beta, &ca2, aoffsi, aoffsj, uppertype==0, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(ca1.ptr.pp_complex[i][j],ca2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        
        /*
         * Test real
         * Only one of transform types is selected and tested
         */
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            rmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rc, xoffsi, xoffsj, 0, beta, &ra1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refrmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rc, xoffsi, xoffsj, 0, beta, &ra2, aoffsi, aoffsj, uppertype==0, _state);
        }
        else
        {
            rmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rct, xoffsj, xoffsi, 1, beta, &ra1, aoffsi, aoffsj, uppertype==0, _state);
            testablasunit_refrmatrixsyrk(n-xoffsi, k-xoffsj, alpha, &rct, xoffsj, xoffsi, 1, beta, &ra2, aoffsi, aoffsj, uppertype==0, _state);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                result = result||ae_fp_greater(ae_fabs(ra1.ptr.pp_double[i][j]-ra2.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
GEMM tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testgemm(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t k;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t aoptype;
    ae_int_t aoptyper;
    ae_int_t boffsi;
    ae_int_t boffsj;
    ae_int_t boptype;
    ae_int_t boptyper;
    ae_int_t coffsi;
    ae_int_t coffsj;
    ae_matrix refra;
    ae_matrix refrb;
    ae_matrix refrc;
    ae_matrix refca;
    ae_matrix refcb;
    ae_matrix refcc;
    double alphar;
    double betar;
    ae_complex alphac;
    ae_complex betac;
    ae_matrix rc1;
    ae_matrix rc2;
    ae_matrix cc1;
    ae_matrix cc2;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcb, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcc, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&rc1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rc2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cc1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cc2, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = maxn*100*ae_machineepsilon;
    result = ae_false;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N/K in [1,MX] such that max(M,N,K)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        k = 1+ae_randominteger(mx, _state);
        i = ae_randominteger(3, _state);
        if( i==0 )
        {
            m = mx;
        }
        if( i==1 )
        {
            n = mx;
        }
        if( i==2 )
        {
            k = mx;
        }
        
        /*
         * Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1)
         */
        ae_matrix_set_length(&refra, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refrb, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refrc, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refca, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refcb, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refcc, maxn+1, maxn+1, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refrb.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refrc.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refcb.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcb.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refcc.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        
        /*
         * test different types of operations, offsets, and so on...
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        ae_matrix_set_length(&rc1, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&rc2, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&cc1, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&cc2, maxn+1, maxn+1, _state);
        aoffsi = ae_randominteger(2, _state);
        aoffsj = ae_randominteger(2, _state);
        aoptype = ae_randominteger(3, _state);
        aoptyper = ae_randominteger(2, _state);
        boffsi = ae_randominteger(2, _state);
        boffsj = ae_randominteger(2, _state);
        boptype = ae_randominteger(3, _state);
        boptyper = ae_randominteger(2, _state);
        coffsi = ae_randominteger(2, _state);
        coffsj = ae_randominteger(2, _state);
        alphar = ae_randominteger(2, _state)*(2*ae_randomreal(_state)-1);
        betar = ae_randominteger(2, _state)*(2*ae_randomreal(_state)-1);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            alphac.x = 2*ae_randomreal(_state)-1;
            alphac.y = 2*ae_randomreal(_state)-1;
        }
        else
        {
            alphac = ae_complex_from_d(0);
        }
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            betac.x = 2*ae_randomreal(_state)-1;
            betac.y = 2*ae_randomreal(_state)-1;
        }
        else
        {
            betac = ae_complex_from_d(0);
        }
        
        /*
         * copy C
         */
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                rc1.ptr.pp_double[i][j] = refrc.ptr.pp_double[i][j];
                rc2.ptr.pp_double[i][j] = refrc.ptr.pp_double[i][j];
                cc1.ptr.pp_complex[i][j] = refcc.ptr.pp_complex[i][j];
                cc2.ptr.pp_complex[i][j] = refcc.ptr.pp_complex[i][j];
            }
        }
        
        /*
         * Test complex
         */
        cmatrixgemm(m, n, k, alphac, &refca, aoffsi, aoffsj, aoptype, &refcb, boffsi, boffsj, boptype, betac, &cc1, coffsi, coffsj, _state);
        testablasunit_refcmatrixgemm(m, n, k, alphac, &refca, aoffsi, aoffsj, aoptype, &refcb, boffsi, boffsj, boptype, betac, &cc2, coffsi, coffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cc1.ptr.pp_complex[i][j],cc2.ptr.pp_complex[i][j]), _state),threshold);
            }
        }
        
        /*
         * Test real
         */
        rmatrixgemm(m, n, k, alphar, &refra, aoffsi, aoffsj, aoptyper, &refrb, boffsi, boffsj, boptyper, betar, &rc1, coffsi, coffsj, _state);
        testablasunit_refrmatrixgemm(m, n, k, alphar, &refra, aoffsi, aoffsj, aoptyper, &refrb, boffsi, boffsj, boptyper, betar, &rc2, coffsi, coffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                result = result||ae_fp_greater(ae_fabs(rc1.ptr.pp_double[i][j]-rc2.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
transpose tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testtrans(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t boffsi;
    ae_int_t boffsj;
    double v1;
    double v2;
    double threshold;
    ae_matrix refra;
    ae_matrix refrb;
    ae_matrix refca;
    ae_matrix refcb;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcb, 0, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         * Generate random V1 and V2 which are used to fill
         * RefRB/RefCB with control values.
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        v1 = ae_randomreal(_state);
        v2 = ae_randomreal(_state);
        
        /*
         * Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
         * Fill B with control values
         */
        ae_matrix_set_length(&refra, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refrb, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refca, maxn+1, maxn+1, _state);
        ae_matrix_set_length(&refcb, maxn+1, maxn+1, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refrb.ptr.pp_double[i][j] = i*v1+j*v2;
                refcb.ptr.pp_complex[i][j] = ae_complex_from_d(i*v1+j*v2);
            }
        }
        
        /*
         * test different offsets (zero or one)
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(2, _state);
        aoffsj = ae_randominteger(2, _state);
        boffsi = ae_randominteger(2, _state);
        boffsj = ae_randominteger(2, _state);
        rmatrixtranspose(m, n, &refra, aoffsi, aoffsj, &refrb, boffsi, boffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                if( ((i<boffsi||i>=boffsi+n)||j<boffsj)||j>=boffsj+m )
                {
                    result = result||ae_fp_greater(ae_fabs(refrb.ptr.pp_double[i][j]-(v1*i+v2*j), _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_fabs(refrb.ptr.pp_double[i][j]-refra.ptr.pp_double[aoffsi+j-boffsj][aoffsj+i-boffsi], _state),threshold);
                }
            }
        }
        cmatrixtranspose(m, n, &refca, aoffsi, aoffsj, &refcb, boffsi, boffsj, _state);
        for(i=0; i<=maxn; i++)
        {
            for(j=0; j<=maxn; j++)
            {
                if( ((i<boffsi||i>=boffsi+n)||j<boffsj)||j>=boffsj+m )
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub_d(refcb.ptr.pp_complex[i][j],v1*i+v2*j), _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(refcb.ptr.pp_complex[i][j],refca.ptr.pp_complex[aoffsi+j-boffsj][aoffsj+i-boffsi]), _state),threshold);
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
rank-1tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testrank1(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t uoffs;
    ae_int_t voffs;
    double threshold;
    ae_matrix refra;
    ae_matrix refrb;
    ae_matrix refca;
    ae_matrix refcb;
    ae_vector ru;
    ae_vector rv;
    ae_vector cu;
    ae_vector cv;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refrb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&refcb, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&ru, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cu, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cv, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
         * Fill B with control values
         */
        ae_matrix_set_length(&refra, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refrb, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refca, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refcb, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                refrb.ptr.pp_double[i][j] = refra.ptr.pp_double[i][j];
                refcb.ptr.pp_complex[i][j] = refca.ptr.pp_complex[i][j];
            }
        }
        ae_vector_set_length(&ru, 2*m, _state);
        ae_vector_set_length(&cu, 2*m, _state);
        for(i=0; i<=2*m-1; i++)
        {
            ru.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            cu.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cu.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&rv, 2*n, _state);
        ae_vector_set_length(&cv, 2*n, _state);
        for(i=0; i<=2*n-1; i++)
        {
            rv.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            cv.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cv.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        
        /*
         * test different offsets (zero or one)
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        uoffs = ae_randominteger(m, _state);
        voffs = ae_randominteger(n, _state);
        cmatrixrank1(m, n, &refca, aoffsi, aoffsj, &cu, uoffs, &cv, voffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<aoffsi||i>=aoffsi+m)||j<aoffsj)||j>=aoffsj+n )
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(refca.ptr.pp_complex[i][j],refcb.ptr.pp_complex[i][j]), _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(refca.ptr.pp_complex[i][j],ae_c_add(refcb.ptr.pp_complex[i][j],ae_c_mul(cu.ptr.p_complex[i-aoffsi+uoffs],cv.ptr.p_complex[j-aoffsj+voffs]))), _state),threshold);
                }
            }
        }
        rmatrixrank1(m, n, &refra, aoffsi, aoffsj, &ru, uoffs, &rv, voffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<aoffsi||i>=aoffsi+m)||j<aoffsj)||j>=aoffsj+n )
                {
                    result = result||ae_fp_greater(ae_fabs(refra.ptr.pp_double[i][j]-refrb.ptr.pp_double[i][j], _state),threshold);
                }
                else
                {
                    result = result||ae_fp_greater(ae_fabs(refra.ptr.pp_double[i][j]-(refrb.ptr.pp_double[i][j]+ru.ptr.p_double[i-aoffsi+uoffs]*rv.ptr.p_double[j-aoffsj+voffs]), _state),threshold);
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
MV tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testmv(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t xoffs;
    ae_int_t yoffs;
    ae_int_t opca;
    ae_int_t opra;
    double threshold;
    double rv1;
    double rv2;
    ae_complex cv1;
    ae_complex cv2;
    ae_matrix refra;
    ae_matrix refca;
    ae_vector rx;
    ae_vector ry;
    ae_vector cx;
    ae_vector cy;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&refra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&refca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&rx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ry, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cx, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cy, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
         * Initialize X by random vector with size (MaxN+MaxN)
         * Fill Y by control values
         */
        ae_matrix_set_length(&refra, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&refca, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                refra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                refca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
        }
        ae_vector_set_length(&rx, 2*maxn, _state);
        ae_vector_set_length(&cx, 2*maxn, _state);
        ae_vector_set_length(&ry, 2*maxn, _state);
        ae_vector_set_length(&cy, 2*maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            rx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            cx.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cx.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            ry.ptr.p_double[i] = i;
            cy.ptr.p_complex[i] = ae_complex_from_d(i);
        }
        
        /*
         * test different offsets (zero or one)
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        xoffs = ae_randominteger(maxn, _state);
        yoffs = ae_randominteger(maxn, _state);
        opca = ae_randominteger(3, _state);
        opra = ae_randominteger(2, _state);
        cmatrixmv(m, n, &refca, aoffsi, aoffsj, opca, &cx, xoffs, &cy, yoffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            if( i<yoffs||i>=yoffs+m )
            {
                result = result||ae_c_neq_d(cy.ptr.p_complex[i],i);
            }
            else
            {
                cv1 = cy.ptr.p_complex[i];
                cv2 = ae_complex_from_d(0.0);
                if( opca==0 )
                {
                    cv2 = ae_v_cdotproduct(&refca.ptr.pp_complex[aoffsi+i-yoffs][aoffsj], 1, "N", &cx.ptr.p_complex[xoffs], 1, "N", ae_v_len(aoffsj,aoffsj+n-1));
                }
                if( opca==1 )
                {
                    cv2 = ae_v_cdotproduct(&refca.ptr.pp_complex[aoffsi][aoffsj+i-yoffs], refca.stride, "N", &cx.ptr.p_complex[xoffs], 1, "N", ae_v_len(aoffsi,aoffsi+n-1));
                }
                if( opca==2 )
                {
                    cv2 = ae_v_cdotproduct(&refca.ptr.pp_complex[aoffsi][aoffsj+i-yoffs], refca.stride, "Conj", &cx.ptr.p_complex[xoffs], 1, "N", ae_v_len(aoffsi,aoffsi+n-1));
                }
                result = result||ae_fp_greater(ae_c_abs(ae_c_sub(cv1,cv2), _state),threshold);
            }
        }
        rmatrixmv(m, n, &refra, aoffsi, aoffsj, opra, &rx, xoffs, &ry, yoffs, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            if( i<yoffs||i>=yoffs+m )
            {
                result = result||ae_fp_neq(ry.ptr.p_double[i],i);
            }
            else
            {
                rv1 = ry.ptr.p_double[i];
                rv2 = 0;
                if( opra==0 )
                {
                    rv2 = ae_v_dotproduct(&refra.ptr.pp_double[aoffsi+i-yoffs][aoffsj], 1, &rx.ptr.p_double[xoffs], 1, ae_v_len(aoffsj,aoffsj+n-1));
                }
                if( opra==1 )
                {
                    rv2 = ae_v_dotproduct(&refra.ptr.pp_double[aoffsi][aoffsj+i-yoffs], refra.stride, &rx.ptr.p_double[xoffs], 1, ae_v_len(aoffsi,aoffsi+n-1));
                }
                result = result||ae_fp_greater(ae_fabs(rv1-rv2, _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
COPY tests

Returns False for passed test, True - for failed
*************************************************************************/
static ae_bool testablasunit_testcopy(ae_int_t minn,
     ae_int_t maxn,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t aoffsi;
    ae_int_t aoffsj;
    ae_int_t boffsi;
    ae_int_t boffsj;
    double threshold;
    ae_matrix ra;
    ae_matrix rb;
    ae_matrix ca;
    ae_matrix cb;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rb, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cb, 0, 0, DT_COMPLEX, _state, ae_true);

    result = ae_false;
    threshold = 1000*ae_machineepsilon;
    for(mx=minn; mx<=maxn; mx++)
    {
        
        /*
         * Select random M/N in [1,MX] such that max(M,N)=MX
         */
        m = 1+ae_randominteger(mx, _state);
        n = 1+ae_randominteger(mx, _state);
        if( ae_randominteger(2, _state)==0 )
        {
            m = mx;
        }
        else
        {
            n = mx;
        }
        
        /*
         * Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
         * Initialize X by random vector with size (MaxN+MaxN)
         * Fill Y by control values
         */
        ae_matrix_set_length(&ra, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&ca, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&rb, maxn+maxn, maxn+maxn, _state);
        ae_matrix_set_length(&cb, maxn+maxn, maxn+maxn, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                rb.ptr.pp_double[i][j] = 1+2*i+3*j;
                cb.ptr.pp_complex[i][j] = ae_complex_from_d(1+2*i+3*j);
            }
        }
        
        /*
         * test different offsets (zero or one)
         *
         * to avoid unnecessary slowdown we don't test ALL possible
         * combinations of operation types. We just generate one random
         * set of parameters and test it.
         */
        aoffsi = ae_randominteger(maxn, _state);
        aoffsj = ae_randominteger(maxn, _state);
        boffsi = ae_randominteger(maxn, _state);
        boffsj = ae_randominteger(maxn, _state);
        cmatrixcopy(m, n, &ca, aoffsi, aoffsj, &cb, boffsi, boffsj, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<boffsi||i>=boffsi+m)||j<boffsj)||j>=boffsj+n )
                {
                    result = result||ae_c_neq_d(cb.ptr.pp_complex[i][j],1+2*i+3*j);
                }
                else
                {
                    result = result||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[aoffsi+i-boffsi][aoffsj+j-boffsj],cb.ptr.pp_complex[i][j]), _state),threshold);
                }
            }
        }
        rmatrixcopy(m, n, &ra, aoffsi, aoffsj, &rb, boffsi, boffsj, _state);
        for(i=0; i<=2*maxn-1; i++)
        {
            for(j=0; j<=2*maxn-1; j++)
            {
                if( ((i<boffsi||i>=boffsi+m)||j<boffsj)||j>=boffsj+n )
                {
                    result = result||ae_fp_neq(rb.ptr.pp_double[i][j],1+2*i+3*j);
                }
                else
                {
                    result = result||ae_fp_greater(ae_fabs(ra.ptr.pp_double[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refcmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_COMPLEX, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    if( isupper )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.pp_complex[i][i] = ae_complex_from_d(1);
        }
    }
    ae_matrix_set_length(&a2, n, n, _state);
    if( optype==0 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[j][i];
            }
        }
        rupper = !rupper;
    }
    if( optype==2 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_complex[i][j] = ae_c_conj(a1.ptr.pp_complex[j][i], _state);
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalcmatrixtrinverse(&a2, n, rupper, ae_false, _state);
    ae_vector_set_length(&tx, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_cmove(&tx.ptr.p_complex[0], 1, &x->ptr.pp_complex[i2+i][j2], 1, "N", ae_v_len(0,n-1));
        for(j=0; j<=n-1; j++)
        {
            vc = ae_v_cdotproduct(&tx.ptr.p_complex[0], 1, "N", &a2.ptr.pp_complex[0][j], a2.stride, "N", ae_v_len(0,n-1));
            x->ptr.pp_complex[i2+i][j2+j] = vc;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refcmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Complex */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_COMPLEX, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, m, m, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            a1.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    if( isupper )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=i; j<=m-1; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_complex[i][j] = a->ptr.pp_complex[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=m-1; i++)
        {
            a1.ptr.pp_complex[i][i] = ae_complex_from_d(1);
        }
    }
    ae_matrix_set_length(&a2, m, m, _state);
    if( optype==0 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_complex[i][j] = a1.ptr.pp_complex[j][i];
            }
        }
        rupper = !rupper;
    }
    if( optype==2 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_complex[i][j] = ae_c_conj(a1.ptr.pp_complex[j][i], _state);
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalcmatrixtrinverse(&a2, m, rupper, ae_false, _state);
    ae_vector_set_length(&tx, m, _state);
    for(j=0; j<=n-1; j++)
    {
        ae_v_cmove(&tx.ptr.p_complex[0], 1, &x->ptr.pp_complex[i2][j2+j], x->stride, "N", ae_v_len(0,m-1));
        for(i=0; i<=m-1; i++)
        {
            vc = ae_v_cdotproduct(&a2.ptr.pp_complex[i][0], 1, "N", &tx.ptr.p_complex[0], 1, "N", ae_v_len(0,m-1));
            x->ptr.pp_complex[i2+i][j2+j] = vc;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refrmatrixrighttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    double vr;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = 0;
        }
    }
    if( isupper )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=i; j<=n-1; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.pp_double[i][i] = 1;
        }
    }
    ae_matrix_set_length(&a2, n, n, _state);
    if( optype==0 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[j][i];
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalrmatrixtrinverse(&a2, n, rupper, ae_false, _state);
    ae_vector_set_length(&tx, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_move(&tx.ptr.p_double[0], 1, &x->ptr.pp_double[i2+i][j2], 1, ae_v_len(0,n-1));
        for(j=0; j<=n-1; j++)
        {
            vr = ae_v_dotproduct(&tx.ptr.p_double[0], 1, &a2.ptr.pp_double[0][j], a2.stride, ae_v_len(0,n-1));
            x->ptr.pp_double[i2+i][j2+j] = vr;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation

  -- ALGLIB routine --
     15.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refrmatrixlefttrsm(ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* a,
     ae_int_t i1,
     ae_int_t j1,
     ae_bool isupper,
     ae_bool isunit,
     ae_int_t optype,
     /* Real    */ ae_matrix* x,
     ae_int_t i2,
     ae_int_t j2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix a2;
    ae_vector tx;
    ae_int_t i;
    ae_int_t j;
    double vr;
    ae_bool rupper;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    if( n*m==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_matrix_set_length(&a1, m, m, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            a1.ptr.pp_double[i][j] = 0;
        }
    }
    if( isupper )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=i; j<=m-1; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    else
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=i; j++)
            {
                a1.ptr.pp_double[i][j] = a->ptr.pp_double[i1+i][j1+j];
            }
        }
    }
    rupper = isupper;
    if( isunit )
    {
        for(i=0; i<=m-1; i++)
        {
            a1.ptr.pp_double[i][i] = 1;
        }
    }
    ae_matrix_set_length(&a2, m, m, _state);
    if( optype==0 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
            }
        }
    }
    if( optype==1 )
    {
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                a2.ptr.pp_double[i][j] = a1.ptr.pp_double[j][i];
            }
        }
        rupper = !rupper;
    }
    testablasunit_internalrmatrixtrinverse(&a2, m, rupper, ae_false, _state);
    ae_vector_set_length(&tx, m, _state);
    for(j=0; j<=n-1; j++)
    {
        ae_v_move(&tx.ptr.p_double[0], 1, &x->ptr.pp_double[i2][j2+j], x->stride, ae_v_len(0,m-1));
        for(i=0; i<=m-1; i++)
        {
            vr = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &tx.ptr.p_double[0], 1, ae_v_len(0,m-1));
            x->ptr.pp_double[i2+i][j2+j] = vr;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Internal subroutine.
Triangular matrix inversion

  -- LAPACK routine (version 3.0) --
     Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
     Courant Institute, Argonne National Lab, and Rice University
     February 29, 1992
*************************************************************************/
static ae_bool testablasunit_internalcmatrixtrinverse(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_complex ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&t, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_d(-1);
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_cmove(&t.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i+1<j )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][i+1], 1, "N", &t.ptr.p_complex[i+1], 1, "N", ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = ae_complex_from_d(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_d(-1);
            }
            if( j+1<n )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_cmove(&t.ptr.p_complex[j+1], 1, &a->ptr.pp_complex[j+1][j], a->stride, "N", ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][j+1], 1, "N", &t.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = ae_complex_from_d(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Internal subroutine.
Triangular matrix inversion

  -- LAPACK routine (version 3.0) --
     Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
     Courant Institute, Argonne National Lab, and Rice University
     February 29, 1992
*************************************************************************/
static ae_bool testablasunit_internalrmatrixtrinverse(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    double v;
    double ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = -1;
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_move(&t.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][i+1], 1, &t.ptr.p_double[i+1], 1, ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = 0;
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = -1;
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_move(&t.ptr.p_double[j+1], 1, &a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &t.ptr.p_double[j+1], 1, ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = 0;
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference SYRK subroutine.

  -- ALGLIB routine --
     16.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refcmatrixsyrk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ae, 0, 0, DT_COMPLEX, _state, ae_true);

    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (isupper&&j>=i)||(!isupper&&j<=i) )
            {
                if( ae_fp_eq(beta,0) )
                {
                    c->ptr.pp_complex[i+ic][j+jc] = ae_complex_from_d(0);
                }
                else
                {
                    c->ptr.pp_complex[i+ic][j+jc] = ae_c_mul_d(c->ptr.pp_complex[i+ic][j+jc],beta);
                }
            }
        }
    }
    if( ae_fp_eq(alpha,0) )
    {
        ae_frame_leave(_state);
        return;
    }
    if( n*k>0 )
    {
        ae_matrix_set_length(&ae, n, k, _state);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_complex[i][j] = a->ptr.pp_complex[ia+i][ja+j];
            }
            if( optypea==2 )
            {
                ae.ptr.pp_complex[i][j] = ae_c_conj(a->ptr.pp_complex[ia+j][ja+i], _state);
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vc = ae_complex_from_d(0);
            if( k>0 )
            {
                vc = ae_v_cdotproduct(&ae.ptr.pp_complex[i][0], 1, "N", &ae.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,k-1));
            }
            vc = ae_c_mul_d(vc,alpha);
            if( isupper&&j>=i )
            {
                c->ptr.pp_complex[ic+i][jc+j] = ae_c_add(vc,c->ptr.pp_complex[ic+i][jc+j]);
            }
            if( !isupper&&j<=i )
            {
                c->ptr.pp_complex[ic+i][jc+j] = ae_c_add(vc,c->ptr.pp_complex[ic+i][jc+j]);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference SYRK subroutine.

  -- ALGLIB routine --
     16.12.2009
     Bochkanov Sergey
*************************************************************************/
static void testablasunit_refrmatrixsyrk(ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_bool isupper,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_int_t i;
    ae_int_t j;
    double vr;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ae, 0, 0, DT_REAL, _state, ae_true);

    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (isupper&&j>=i)||(!isupper&&j<=i) )
            {
                if( ae_fp_eq(beta,0) )
                {
                    c->ptr.pp_double[i+ic][j+jc] = 0;
                }
                else
                {
                    c->ptr.pp_double[i+ic][j+jc] = c->ptr.pp_double[i+ic][j+jc]*beta;
                }
            }
        }
    }
    if( ae_fp_eq(alpha,0) )
    {
        ae_frame_leave(_state);
        return;
    }
    if( n*k>0 )
    {
        ae_matrix_set_length(&ae, n, k, _state);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
            }
            if( optypea==1 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+j][ja+i];
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vr = 0;
            if( k>0 )
            {
                vr = ae_v_dotproduct(&ae.ptr.pp_double[i][0], 1, &ae.ptr.pp_double[j][0], 1, ae_v_len(0,k-1));
            }
            vr = alpha*vr;
            if( isupper&&j>=i )
            {
                c->ptr.pp_double[ic+i][jc+j] = vr+c->ptr.pp_double[ic+i][jc+j];
            }
            if( !isupper&&j<=i )
            {
                c->ptr.pp_double[ic+i][jc+j] = vr+c->ptr.pp_double[ic+i][jc+j];
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference GEMM,
ALGLIB subroutine
*************************************************************************/
static void testablasunit_refcmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     ae_complex alpha,
     /* Complex */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Complex */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     ae_complex beta,
     /* Complex */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_matrix be;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ae, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&be, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&ae, m, k, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_complex[i][j] = a->ptr.pp_complex[ia+i][ja+j];
            }
            if( optypea==1 )
            {
                ae.ptr.pp_complex[i][j] = a->ptr.pp_complex[ia+j][ja+i];
            }
            if( optypea==2 )
            {
                ae.ptr.pp_complex[i][j] = ae_c_conj(a->ptr.pp_complex[ia+j][ja+i], _state);
            }
        }
    }
    ae_matrix_set_length(&be, k, n, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( optypeb==0 )
            {
                be.ptr.pp_complex[i][j] = b->ptr.pp_complex[ib+i][jb+j];
            }
            if( optypeb==1 )
            {
                be.ptr.pp_complex[i][j] = b->ptr.pp_complex[ib+j][jb+i];
            }
            if( optypeb==2 )
            {
                be.ptr.pp_complex[i][j] = ae_c_conj(b->ptr.pp_complex[ib+j][jb+i], _state);
            }
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vc = ae_v_cdotproduct(&ae.ptr.pp_complex[i][0], 1, "N", &be.ptr.pp_complex[0][j], be.stride, "N", ae_v_len(0,k-1));
            vc = ae_c_mul(alpha,vc);
            if( ae_c_neq_d(beta,0) )
            {
                vc = ae_c_add(vc,ae_c_mul(beta,c->ptr.pp_complex[ic+i][jc+j]));
            }
            c->ptr.pp_complex[ic+i][jc+j] = vc;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference GEMM,
ALGLIB subroutine
*************************************************************************/
static void testablasunit_refrmatrixgemm(ae_int_t m,
     ae_int_t n,
     ae_int_t k,
     double alpha,
     /* Real    */ ae_matrix* a,
     ae_int_t ia,
     ae_int_t ja,
     ae_int_t optypea,
     /* Real    */ ae_matrix* b,
     ae_int_t ib,
     ae_int_t jb,
     ae_int_t optypeb,
     double beta,
     /* Real    */ ae_matrix* c,
     ae_int_t ic,
     ae_int_t jc,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ae;
    ae_matrix be;
    ae_int_t i;
    ae_int_t j;
    double vc;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ae, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&be, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&ae, m, k, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            if( optypea==0 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+i][ja+j];
            }
            if( optypea==1 )
            {
                ae.ptr.pp_double[i][j] = a->ptr.pp_double[ia+j][ja+i];
            }
        }
    }
    ae_matrix_set_length(&be, k, n, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( optypeb==0 )
            {
                be.ptr.pp_double[i][j] = b->ptr.pp_double[ib+i][jb+j];
            }
            if( optypeb==1 )
            {
                be.ptr.pp_double[i][j] = b->ptr.pp_double[ib+j][jb+i];
            }
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            vc = ae_v_dotproduct(&ae.ptr.pp_double[i][0], 1, &be.ptr.pp_double[0][j], be.stride, ae_v_len(0,k-1));
            vc = alpha*vc;
            if( ae_fp_neq(beta,0) )
            {
                vc = vc+beta*c->ptr.pp_double[ic+i][jc+j];
            }
            c->ptr.pp_double[ic+i][jc+j] = vc;
        }
    }
    ae_frame_leave(_state);
}








ae_bool testbasestat(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool s1errors;
    ae_bool covcorrerrors;
    double threshold;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t kx;
    ae_int_t ky;
    ae_int_t ctype;
    ae_int_t cidxx;
    ae_int_t cidxy;
    ae_vector x;
    ae_vector y;
    ae_matrix mx;
    ae_matrix my;
    ae_matrix cc;
    ae_matrix cp;
    ae_matrix cs;
    double mean;
    double variance;
    double skewness;
    double kurtosis;
    double adev;
    double median;
    double pv;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mx, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&my, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cc, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cp, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cs, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Primary settings
     */
    waserrors = ae_false;
    s1errors = ae_false;
    covcorrerrors = ae_false;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * * prepare X and Y - two test samples
     * * test 1-sample coefficients
     */
    n = 10;
    ae_vector_set_length(&x, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_sqr(i, _state);
    }
    samplemoments(&x, n, &mean, &variance, &skewness, &kurtosis, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(mean-28.5, _state),0.001);
    s1errors = s1errors||ae_fp_greater(ae_fabs(variance-801.1667, _state),0.001);
    s1errors = s1errors||ae_fp_greater(ae_fabs(skewness-0.5751, _state),0.001);
    s1errors = s1errors||ae_fp_greater(ae_fabs(kurtosis+1.2666, _state),0.001);
    sampleadev(&x, n, &adev, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(adev-23.2000, _state),0.001);
    samplemedian(&x, n, &median, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(median-0.5*(16+25), _state),0.001);
    for(i=0; i<=n-1; i++)
    {
        samplepercentile(&x, n, (double)i/(double)(n-1), &pv, _state);
        s1errors = s1errors||ae_fp_greater(ae_fabs(pv-x.ptr.p_double[i], _state),0.001);
    }
    samplepercentile(&x, n, 0.5, &pv, _state);
    s1errors = s1errors||ae_fp_greater(ae_fabs(pv-0.5*(16+25), _state),0.001);
    
    /*
     * test covariance/correlation:
     * * 2-sample coefficients
     *
     * We generate random matrices MX and MY
     */
    n = 10;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_sqr(i, _state);
        y.ptr.p_double[i] = i;
    }
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)-0.9627, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)-1.0000, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)-82.5000, _state),0.0001);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = ae_sqr(i-0.5*n, _state);
        y.ptr.p_double[i] = i;
    }
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)+0.3676, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)+0.2761, _state),0.0001);
    covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)+9.1667, _state),0.0001);
    
    /*
     * test covariance/correlation:
     * * matrix covariance/correlation
     * * matrix cross-covariance/cross-correlation
     *
     * We generate random matrices MX and MY which contain KX (KY)
     * columns, all except one are random, one of them is constant.
     * We test that function (a) do not crash on constant column,
     * and (b) return variances/correlations that are exactly zero
     * for this column.
     *
     * CType control variable controls type of constant: 0 - no constant
     * column, 1 - zero column, 2 - nonzero column with value whose
     * binary representation contains many non-zero bits. Using such
     * type of constant column we are able to ensure than even in the
     * presense of roundoff error functions correctly detect constant
     * columns.
     */
    for(n=0; n<=10; n++)
    {
        if( n>0 )
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
        }
        for(ctype=0; ctype<=2; ctype++)
        {
            for(kx=1; kx<=10; kx++)
            {
                for(ky=1; ky<=10; ky++)
                {
                    
                    /*
                     * Fill matrices, add constant column (when CType=1 or =2)
                     */
                    cidxx = -1;
                    cidxy = -1;
                    if( n>0 )
                    {
                        ae_matrix_set_length(&mx, n, kx, _state);
                        ae_matrix_set_length(&my, n, ky, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=kx-1; j++)
                            {
                                mx.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                            for(j=0; j<=ky-1; j++)
                            {
                                my.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        if( ctype==1 )
                        {
                            cidxx = ae_randominteger(kx, _state);
                            cidxy = ae_randominteger(ky, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                mx.ptr.pp_double[i][cidxx] = 0.0;
                                my.ptr.pp_double[i][cidxy] = 0.0;
                            }
                        }
                        if( ctype==2 )
                        {
                            cidxx = ae_randominteger(kx, _state);
                            cidxy = ae_randominteger(ky, _state);
                            v = ae_sqrt((double)(ae_randominteger(kx, _state)+1)/(double)kx, _state);
                            for(i=0; i<=n-1; i++)
                            {
                                mx.ptr.pp_double[i][cidxx] = v;
                                my.ptr.pp_double[i][cidxy] = v;
                            }
                        }
                    }
                    
                    /*
                     * test covariance/correlation matrix using
                     * 2-sample functions as reference point.
                     *
                     * We also test that coefficients for constant variables
                     * are exactly zero.
                     */
                    covm(&mx, n, kx, &cc, _state);
                    pearsoncorrm(&mx, n, kx, &cp, _state);
                    spearmancorrm(&mx, n, kx, &cs, _state);
                    for(i=0; i<=kx-1; i++)
                    {
                        for(j=0; j<=kx-1; j++)
                        {
                            if( n>0 )
                            {
                                ae_v_move(&x.ptr.p_double[0], 1, &mx.ptr.pp_double[0][i], mx.stride, ae_v_len(0,n-1));
                                ae_v_move(&y.ptr.p_double[0], 1, &mx.ptr.pp_double[0][j], mx.stride, ae_v_len(0,n-1));
                            }
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)-cc.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)-cp.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)-cs.ptr.pp_double[i][j], _state),threshold);
                        }
                    }
                    if( ctype!=0&&n>0 )
                    {
                        for(i=0; i<=kx-1; i++)
                        {
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[i][cidxx],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[cidxx][i],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[i][cidxx],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[cidxx][i],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[i][cidxx],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[cidxx][i],0);
                        }
                    }
                    
                    /*
                     * test cross-covariance/cross-correlation matrix using
                     * 2-sample functions as reference point.
                     *
                     * We also test that coefficients for constant variables
                     * are exactly zero.
                     */
                    covm2(&mx, &my, n, kx, ky, &cc, _state);
                    pearsoncorrm2(&mx, &my, n, kx, ky, &cp, _state);
                    spearmancorrm2(&mx, &my, n, kx, ky, &cs, _state);
                    for(i=0; i<=kx-1; i++)
                    {
                        for(j=0; j<=ky-1; j++)
                        {
                            if( n>0 )
                            {
                                ae_v_move(&x.ptr.p_double[0], 1, &mx.ptr.pp_double[0][i], mx.stride, ae_v_len(0,n-1));
                                ae_v_move(&y.ptr.p_double[0], 1, &my.ptr.pp_double[0][j], my.stride, ae_v_len(0,n-1));
                            }
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(cov2(&x, &y, n, _state)-cc.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(pearsoncorr2(&x, &y, n, _state)-cp.ptr.pp_double[i][j], _state),threshold);
                            covcorrerrors = covcorrerrors||ae_fp_greater(ae_fabs(spearmancorr2(&x, &y, n, _state)-cs.ptr.pp_double[i][j], _state),threshold);
                        }
                    }
                    if( ctype!=0&&n>0 )
                    {
                        for(i=0; i<=kx-1; i++)
                        {
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[i][cidxy],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[i][cidxy],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[i][cidxy],0);
                        }
                        for(j=0; j<=ky-1; j++)
                        {
                            covcorrerrors = covcorrerrors||ae_fp_neq(cc.ptr.pp_double[cidxx][j],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cp.ptr.pp_double[cidxx][j],0);
                            covcorrerrors = covcorrerrors||ae_fp_neq(cs.ptr.pp_double[cidxx][j],0);
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Final report
     */
    waserrors = s1errors||covcorrerrors;
    if( !silent )
    {
        printf("DESC.STAT TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* 1-SAMPLE FUNCTIONALITY:                ");
        if( !s1errors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* CORRELATION/COVARIATION:               ");
        if( !covcorrerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testbdssunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state);
static void testbdssunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state);
static void testbdssunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state);
static void testbdssunit_testsortresults(/* Real    */ ae_vector* asorted,
     /* Integer */ ae_vector* p1,
     /* Integer */ ae_vector* p2,
     /* Real    */ ae_vector* aoriginal,
     ae_int_t n,
     ae_bool* waserrors,
     ae_state *_state);





/*************************************************************************
Testing BDSS operations
*************************************************************************/
ae_bool testbdss(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t maxnq;
    ae_vector a;
    ae_vector a0;
    ae_vector at;
    ae_matrix p;
    ae_vector thresholds;
    ae_int_t ni;
    ae_vector c;
    ae_vector p1;
    ae_vector p2;
    ae_vector ties;
    ae_vector pt1;
    ae_vector pt2;
    ae_int_t tiecount;
    ae_int_t c1;
    ae_int_t c0;
    ae_int_t nc;
    ae_vector tmp;
    ae_vector sortrbuf;
    ae_vector sortrbuf2;
    ae_vector sortibuf;
    double pal;
    double pbl;
    double par;
    double pbr;
    double cve;
    double cvr;
    ae_int_t info;
    double threshold;
    ae_vector tiebuf;
    ae_vector cntbuf;
    double rms;
    double cvrms;
    ae_bool waserrors;
    ae_bool tieserrors;
    ae_bool split2errors;
    ae_bool optimalsplitkerrors;
    ae_bool splitkerrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&at, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&thresholds, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&ties, 0, DT_INT, _state, ae_true);
    ae_vector_init(&pt1, 0, DT_INT, _state, ae_true);
    ae_vector_init(&pt2, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sortrbuf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sortrbuf2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sortibuf, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tiebuf, 0, DT_INT, _state, ae_true);
    ae_vector_init(&cntbuf, 0, DT_INT, _state, ae_true);

    waserrors = ae_false;
    tieserrors = ae_false;
    split2errors = ae_false;
    splitkerrors = ae_false;
    optimalsplitkerrors = ae_false;
    maxn = 100;
    maxnq = 49;
    passcount = 10;
    
    /*
     * Test ties
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * untied data, test DSTie
             */
            testbdssunit_unset1di(&p1, _state);
            testbdssunit_unset1di(&p2, _state);
            testbdssunit_unset1di(&pt1, _state);
            testbdssunit_unset1di(&pt2, _state);
            ae_vector_set_length(&a, n-1+1, _state);
            ae_vector_set_length(&a0, n-1+1, _state);
            ae_vector_set_length(&at, n-1+1, _state);
            ae_vector_set_length(&tmp, n-1+1, _state);
            a.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
            tmp.ptr.p_double[0] = ae_randomreal(_state);
            for(i=1; i<=n-1; i++)
            {
                
                /*
                 * A is randomly permuted
                 */
                a.ptr.p_double[i] = a.ptr.p_double[i-1]+0.1*ae_randomreal(_state)+0.1;
                tmp.ptr.p_double[i] = ae_randomreal(_state);
            }
            tagsortfastr(&tmp, &a, &sortrbuf, &sortrbuf2, n, _state);
            for(i=0; i<=n-1; i++)
            {
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                at.ptr.p_double[i] = a.ptr.p_double[i];
            }
            dstie(&a0, n, &ties, &tiecount, &p1, &p2, _state);
            tagsort(&at, n, &pt1, &pt2, _state);
            for(i=0; i<=n-1; i++)
            {
                tieserrors = tieserrors||p1.ptr.p_int[i]!=pt1.ptr.p_int[i];
                tieserrors = tieserrors||p2.ptr.p_int[i]!=pt2.ptr.p_int[i];
            }
            tieserrors = tieserrors||tiecount!=n;
            if( tiecount==n )
            {
                for(i=0; i<=n; i++)
                {
                    tieserrors = tieserrors||ties.ptr.p_int[i]!=i;
                }
            }
            
            /*
             * tied data, test DSTie
             */
            testbdssunit_unset1di(&p1, _state);
            testbdssunit_unset1di(&p2, _state);
            testbdssunit_unset1di(&pt1, _state);
            testbdssunit_unset1di(&pt2, _state);
            ae_vector_set_length(&a, n-1+1, _state);
            ae_vector_set_length(&a0, n-1+1, _state);
            ae_vector_set_length(&at, n-1+1, _state);
            c1 = 0;
            c0 = 0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = ae_randominteger(2, _state);
                if( ae_fp_eq(a.ptr.p_double[i],0) )
                {
                    c0 = c0+1;
                }
                else
                {
                    c1 = c1+1;
                }
                a0.ptr.p_double[i] = a.ptr.p_double[i];
                at.ptr.p_double[i] = a.ptr.p_double[i];
            }
            dstie(&a0, n, &ties, &tiecount, &p1, &p2, _state);
            tagsort(&at, n, &pt1, &pt2, _state);
            for(i=0; i<=n-1; i++)
            {
                tieserrors = tieserrors||p1.ptr.p_int[i]!=pt1.ptr.p_int[i];
                tieserrors = tieserrors||p2.ptr.p_int[i]!=pt2.ptr.p_int[i];
            }
            if( c0==0||c1==0 )
            {
                tieserrors = tieserrors||tiecount!=1;
                if( tiecount==1 )
                {
                    tieserrors = tieserrors||ties.ptr.p_int[0]!=0;
                    tieserrors = tieserrors||ties.ptr.p_int[1]!=n;
                }
            }
            else
            {
                tieserrors = tieserrors||tiecount!=2;
                if( tiecount==2 )
                {
                    tieserrors = tieserrors||ties.ptr.p_int[0]!=0;
                    tieserrors = tieserrors||ties.ptr.p_int[1]!=c0;
                    tieserrors = tieserrors||ties.ptr.p_int[2]!=n;
                }
            }
        }
    }
    
    /*
     * split-2
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxn; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = n;
                c.ptr.p_int[i] = i%2;
            }
            dsoptimalsplit2(&a, &c, n, &info, &threshold, &pal, &pbl, &par, &pbr, &cve, _state);
            if( info!=-3 )
            {
                split2errors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = i/((n+1)/2);
                c.ptr.p_int[i] = i/((n+1)/2);
            }
            dsoptimalsplit2(&a, &c, n, &info, &threshold, &pal, &pbl, &par, &pbr, &cve, _state);
            if( info!=1 )
            {
                split2errors = ae_true;
                continue;
            }
            split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-0.5, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(pal-1, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(pbl-0, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(par-0, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(pbr-1, _state),100*ae_machineepsilon);
        }
    }
    
    /*
     * Special "CREDIT"-test (transparency coefficient)
     */
    n = 110;
    ae_vector_set_length(&a, n-1+1, _state);
    ae_vector_set_length(&c, n-1+1, _state);
    a.ptr.p_double[0] = 0.000;
    c.ptr.p_int[0] = 0;
    a.ptr.p_double[1] = 0.000;
    c.ptr.p_int[1] = 0;
    a.ptr.p_double[2] = 0.000;
    c.ptr.p_int[2] = 0;
    a.ptr.p_double[3] = 0.000;
    c.ptr.p_int[3] = 0;
    a.ptr.p_double[4] = 0.000;
    c.ptr.p_int[4] = 0;
    a.ptr.p_double[5] = 0.000;
    c.ptr.p_int[5] = 0;
    a.ptr.p_double[6] = 0.000;
    c.ptr.p_int[6] = 0;
    a.ptr.p_double[7] = 0.000;
    c.ptr.p_int[7] = 1;
    a.ptr.p_double[8] = 0.000;
    c.ptr.p_int[8] = 0;
    a.ptr.p_double[9] = 0.000;
    c.ptr.p_int[9] = 1;
    a.ptr.p_double[10] = 0.000;
    c.ptr.p_int[10] = 0;
    a.ptr.p_double[11] = 0.000;
    c.ptr.p_int[11] = 0;
    a.ptr.p_double[12] = 0.000;
    c.ptr.p_int[12] = 0;
    a.ptr.p_double[13] = 0.000;
    c.ptr.p_int[13] = 0;
    a.ptr.p_double[14] = 0.000;
    c.ptr.p_int[14] = 0;
    a.ptr.p_double[15] = 0.000;
    c.ptr.p_int[15] = 0;
    a.ptr.p_double[16] = 0.000;
    c.ptr.p_int[16] = 0;
    a.ptr.p_double[17] = 0.000;
    c.ptr.p_int[17] = 0;
    a.ptr.p_double[18] = 0.000;
    c.ptr.p_int[18] = 0;
    a.ptr.p_double[19] = 0.000;
    c.ptr.p_int[19] = 0;
    a.ptr.p_double[20] = 0.000;
    c.ptr.p_int[20] = 0;
    a.ptr.p_double[21] = 0.000;
    c.ptr.p_int[21] = 0;
    a.ptr.p_double[22] = 0.000;
    c.ptr.p_int[22] = 1;
    a.ptr.p_double[23] = 0.000;
    c.ptr.p_int[23] = 0;
    a.ptr.p_double[24] = 0.000;
    c.ptr.p_int[24] = 0;
    a.ptr.p_double[25] = 0.000;
    c.ptr.p_int[25] = 0;
    a.ptr.p_double[26] = 0.000;
    c.ptr.p_int[26] = 0;
    a.ptr.p_double[27] = 0.000;
    c.ptr.p_int[27] = 1;
    a.ptr.p_double[28] = 0.000;
    c.ptr.p_int[28] = 0;
    a.ptr.p_double[29] = 0.000;
    c.ptr.p_int[29] = 1;
    a.ptr.p_double[30] = 0.000;
    c.ptr.p_int[30] = 0;
    a.ptr.p_double[31] = 0.000;
    c.ptr.p_int[31] = 1;
    a.ptr.p_double[32] = 0.000;
    c.ptr.p_int[32] = 0;
    a.ptr.p_double[33] = 0.000;
    c.ptr.p_int[33] = 1;
    a.ptr.p_double[34] = 0.000;
    c.ptr.p_int[34] = 0;
    a.ptr.p_double[35] = 0.030;
    c.ptr.p_int[35] = 0;
    a.ptr.p_double[36] = 0.030;
    c.ptr.p_int[36] = 0;
    a.ptr.p_double[37] = 0.050;
    c.ptr.p_int[37] = 0;
    a.ptr.p_double[38] = 0.070;
    c.ptr.p_int[38] = 1;
    a.ptr.p_double[39] = 0.110;
    c.ptr.p_int[39] = 0;
    a.ptr.p_double[40] = 0.110;
    c.ptr.p_int[40] = 1;
    a.ptr.p_double[41] = 0.120;
    c.ptr.p_int[41] = 0;
    a.ptr.p_double[42] = 0.130;
    c.ptr.p_int[42] = 0;
    a.ptr.p_double[43] = 0.140;
    c.ptr.p_int[43] = 0;
    a.ptr.p_double[44] = 0.140;
    c.ptr.p_int[44] = 0;
    a.ptr.p_double[45] = 0.140;
    c.ptr.p_int[45] = 0;
    a.ptr.p_double[46] = 0.150;
    c.ptr.p_int[46] = 0;
    a.ptr.p_double[47] = 0.150;
    c.ptr.p_int[47] = 0;
    a.ptr.p_double[48] = 0.170;
    c.ptr.p_int[48] = 0;
    a.ptr.p_double[49] = 0.190;
    c.ptr.p_int[49] = 1;
    a.ptr.p_double[50] = 0.200;
    c.ptr.p_int[50] = 0;
    a.ptr.p_double[51] = 0.200;
    c.ptr.p_int[51] = 0;
    a.ptr.p_double[52] = 0.250;
    c.ptr.p_int[52] = 0;
    a.ptr.p_double[53] = 0.250;
    c.ptr.p_int[53] = 0;
    a.ptr.p_double[54] = 0.260;
    c.ptr.p_int[54] = 0;
    a.ptr.p_double[55] = 0.270;
    c.ptr.p_int[55] = 0;
    a.ptr.p_double[56] = 0.280;
    c.ptr.p_int[56] = 0;
    a.ptr.p_double[57] = 0.310;
    c.ptr.p_int[57] = 0;
    a.ptr.p_double[58] = 0.310;
    c.ptr.p_int[58] = 0;
    a.ptr.p_double[59] = 0.330;
    c.ptr.p_int[59] = 0;
    a.ptr.p_double[60] = 0.330;
    c.ptr.p_int[60] = 0;
    a.ptr.p_double[61] = 0.340;
    c.ptr.p_int[61] = 0;
    a.ptr.p_double[62] = 0.340;
    c.ptr.p_int[62] = 0;
    a.ptr.p_double[63] = 0.370;
    c.ptr.p_int[63] = 0;
    a.ptr.p_double[64] = 0.380;
    c.ptr.p_int[64] = 1;
    a.ptr.p_double[65] = 0.380;
    c.ptr.p_int[65] = 0;
    a.ptr.p_double[66] = 0.410;
    c.ptr.p_int[66] = 0;
    a.ptr.p_double[67] = 0.460;
    c.ptr.p_int[67] = 0;
    a.ptr.p_double[68] = 0.520;
    c.ptr.p_int[68] = 0;
    a.ptr.p_double[69] = 0.530;
    c.ptr.p_int[69] = 0;
    a.ptr.p_double[70] = 0.540;
    c.ptr.p_int[70] = 0;
    a.ptr.p_double[71] = 0.560;
    c.ptr.p_int[71] = 0;
    a.ptr.p_double[72] = 0.560;
    c.ptr.p_int[72] = 0;
    a.ptr.p_double[73] = 0.570;
    c.ptr.p_int[73] = 0;
    a.ptr.p_double[74] = 0.600;
    c.ptr.p_int[74] = 0;
    a.ptr.p_double[75] = 0.600;
    c.ptr.p_int[75] = 0;
    a.ptr.p_double[76] = 0.620;
    c.ptr.p_int[76] = 0;
    a.ptr.p_double[77] = 0.650;
    c.ptr.p_int[77] = 0;
    a.ptr.p_double[78] = 0.660;
    c.ptr.p_int[78] = 0;
    a.ptr.p_double[79] = 0.680;
    c.ptr.p_int[79] = 0;
    a.ptr.p_double[80] = 0.700;
    c.ptr.p_int[80] = 0;
    a.ptr.p_double[81] = 0.750;
    c.ptr.p_int[81] = 0;
    a.ptr.p_double[82] = 0.770;
    c.ptr.p_int[82] = 0;
    a.ptr.p_double[83] = 0.770;
    c.ptr.p_int[83] = 0;
    a.ptr.p_double[84] = 0.770;
    c.ptr.p_int[84] = 0;
    a.ptr.p_double[85] = 0.790;
    c.ptr.p_int[85] = 0;
    a.ptr.p_double[86] = 0.810;
    c.ptr.p_int[86] = 0;
    a.ptr.p_double[87] = 0.840;
    c.ptr.p_int[87] = 0;
    a.ptr.p_double[88] = 0.860;
    c.ptr.p_int[88] = 0;
    a.ptr.p_double[89] = 0.870;
    c.ptr.p_int[89] = 0;
    a.ptr.p_double[90] = 0.890;
    c.ptr.p_int[90] = 0;
    a.ptr.p_double[91] = 0.900;
    c.ptr.p_int[91] = 1;
    a.ptr.p_double[92] = 0.900;
    c.ptr.p_int[92] = 0;
    a.ptr.p_double[93] = 0.910;
    c.ptr.p_int[93] = 0;
    a.ptr.p_double[94] = 0.940;
    c.ptr.p_int[94] = 0;
    a.ptr.p_double[95] = 0.950;
    c.ptr.p_int[95] = 0;
    a.ptr.p_double[96] = 0.952;
    c.ptr.p_int[96] = 0;
    a.ptr.p_double[97] = 0.970;
    c.ptr.p_int[97] = 0;
    a.ptr.p_double[98] = 0.970;
    c.ptr.p_int[98] = 0;
    a.ptr.p_double[99] = 0.980;
    c.ptr.p_int[99] = 0;
    a.ptr.p_double[100] = 1.000;
    c.ptr.p_int[100] = 0;
    a.ptr.p_double[101] = 1.000;
    c.ptr.p_int[101] = 0;
    a.ptr.p_double[102] = 1.000;
    c.ptr.p_int[102] = 0;
    a.ptr.p_double[103] = 1.000;
    c.ptr.p_int[103] = 0;
    a.ptr.p_double[104] = 1.000;
    c.ptr.p_int[104] = 0;
    a.ptr.p_double[105] = 1.020;
    c.ptr.p_int[105] = 0;
    a.ptr.p_double[106] = 1.090;
    c.ptr.p_int[106] = 0;
    a.ptr.p_double[107] = 1.130;
    c.ptr.p_int[107] = 0;
    a.ptr.p_double[108] = 1.840;
    c.ptr.p_int[108] = 0;
    a.ptr.p_double[109] = 2.470;
    c.ptr.p_int[109] = 0;
    dsoptimalsplit2(&a, &c, n, &info, &threshold, &pal, &pbl, &par, &pbr, &cve, _state);
    if( info!=1 )
    {
        split2errors = ae_true;
    }
    else
    {
        split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-0.195, _state),100*ae_machineepsilon);
        split2errors = split2errors||ae_fp_greater(ae_fabs(pal-0.80, _state),0.02);
        split2errors = split2errors||ae_fp_greater(ae_fabs(pbl-0.20, _state),0.02);
        split2errors = split2errors||ae_fp_greater(ae_fabs(par-0.97, _state),0.02);
        split2errors = split2errors||ae_fp_greater(ae_fabs(pbr-0.03, _state),0.02);
    }
    
    /*
     * split-2 fast
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxn; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        ae_vector_set_length(&tiebuf, n+1, _state);
        ae_vector_set_length(&cntbuf, 3+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = n;
                c.ptr.p_int[i] = i%2;
            }
            dsoptimalsplit2fast(&a, &c, &tiebuf, &cntbuf, &sortrbuf, &sortibuf, n, 2, 0.00, &info, &threshold, &rms, &cvrms, _state);
            if( info!=-3 )
            {
                split2errors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = i/((n+1)/2);
                c.ptr.p_int[i] = i/((n+1)/2);
            }
            dsoptimalsplit2fast(&a, &c, &tiebuf, &cntbuf, &sortrbuf, &sortibuf, n, 2, 0.00, &info, &threshold, &rms, &cvrms, _state);
            if( info!=1 )
            {
                split2errors = ae_true;
                continue;
            }
            split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-0.5, _state),100*ae_machineepsilon);
            split2errors = split2errors||ae_fp_greater(ae_fabs(rms-0, _state),100*ae_machineepsilon);
            if( n==2 )
            {
                split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms-0.5, _state),100*ae_machineepsilon);
            }
            else
            {
                if( n==3 )
                {
                    split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms-ae_sqrt((2*0+2*0+2*0.25)/6, _state), _state),100*ae_machineepsilon);
                }
                else
                {
                    split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms, _state),100*ae_machineepsilon);
                }
            }
        }
    }
    
    /*
     * special tests
     */
    n = 10;
    ae_vector_set_length(&a, n-1+1, _state);
    ae_vector_set_length(&c, n-1+1, _state);
    ae_vector_set_length(&tiebuf, n+1, _state);
    ae_vector_set_length(&cntbuf, 2*3-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        a.ptr.p_double[i] = i;
        if( i<=n-3 )
        {
            c.ptr.p_int[i] = 0;
        }
        else
        {
            c.ptr.p_int[i] = i-(n-3);
        }
    }
    dsoptimalsplit2fast(&a, &c, &tiebuf, &cntbuf, &sortrbuf, &sortibuf, n, 3, 0.00, &info, &threshold, &rms, &cvrms, _state);
    if( info!=1 )
    {
        split2errors = ae_true;
    }
    else
    {
        split2errors = split2errors||ae_fp_greater(ae_fabs(threshold-(n-2.5), _state),100*ae_machineepsilon);
        split2errors = split2errors||ae_fp_greater(ae_fabs(rms-ae_sqrt((0.25+0.25+0.25+0.25)/(3*n), _state), _state),100*ae_machineepsilon);
        split2errors = split2errors||ae_fp_greater(ae_fabs(cvrms-ae_sqrt((double)(1+1+1+1)/(double)(3*n), _state), _state),100*ae_machineepsilon);
    }
    
    /*
     * Optimal split-K
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxnq; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = n;
                c.ptr.p_int[i] = i%2;
            }
            dsoptimalsplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=-3 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            c0 = 0;
            c1 = 0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = i/((n+1)/2);
                c.ptr.p_int[i] = i/((n+1)/2);
                if( c.ptr.p_int[i]==0 )
                {
                    c0 = c0+1;
                }
                if( c.ptr.p_int[i]==1 )
                {
                    c1 = c1+1;
                }
            }
            dsoptimalsplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
            optimalsplitkerrors = optimalsplitkerrors||ni!=2;
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
        }
        
        /*
         * test #2
         */
        if( n>2 )
        {
            c0 = 1+ae_randominteger(n-1, _state);
            c1 = n-c0;
            for(i=0; i<=n-1; i++)
            {
                if( i<c0 )
                {
                    a.ptr.p_double[i] = 0;
                    c.ptr.p_int[i] = 0;
                }
                else
                {
                    a.ptr.p_double[i] = 1;
                    c.ptr.p_int[i] = 1;
                }
            }
            dsoptimalsplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
            optimalsplitkerrors = optimalsplitkerrors||ni!=2;
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
            optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
        }
        
        /*
         * multi-tie test
         */
        if( n>=16 )
        {
            
            /*
             * Multi-tie test.
             *
             * First NC-1 ties have C0 entries, remaining NC-th tie
             * have C1 entries.
             */
            nc = ae_round(ae_sqrt(n, _state), _state);
            c0 = n/nc;
            c1 = n-c0*(nc-1);
            for(i=0; i<=nc-2; i++)
            {
                for(j=c0*i; j<=c0*(i+1)-1; j++)
                {
                    a.ptr.p_double[j] = j;
                    c.ptr.p_int[j] = i;
                }
            }
            for(j=c0*(nc-1); j<=n-1; j++)
            {
                a.ptr.p_double[j] = j;
                c.ptr.p_int[j] = nc-1;
            }
            dsoptimalsplitk(&a, &c, n, nc, nc+ae_randominteger(nc, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                optimalsplitkerrors = ae_true;
                continue;
            }
            optimalsplitkerrors = optimalsplitkerrors||ni!=nc;
            if( ni==nc )
            {
                for(i=0; i<=nc-2; i++)
                {
                    optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[i]-(c0*(i+1)-1+0.5), _state),100*ae_machineepsilon);
                }
                cvr = -((nc-1)*c0*ae_log((double)c0/(double)(c0+nc-1), _state)+c1*ae_log((double)c1/(double)(c1+nc-1), _state));
                optimalsplitkerrors = optimalsplitkerrors||ae_fp_greater(ae_fabs(cve-cvr, _state),100*ae_machineepsilon);
            }
        }
    }
    
    /*
     * Non-optimal split-K
     */
    
    /*
     * General tests for different N's
     */
    for(n=1; n<=maxnq; n++)
    {
        ae_vector_set_length(&a, n-1+1, _state);
        ae_vector_set_length(&c, n-1+1, _state);
        
        /*
         * one-tie test
         */
        if( n%2==0 )
        {
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = pass;
                c.ptr.p_int[i] = i%2;
            }
            dssplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=-3 )
            {
                splitkerrors = ae_true;
                continue;
            }
        }
        
        /*
         * two-tie test
         */
        
        /*
         * test #1
         */
        if( n>1 )
        {
            c0 = 0;
            c1 = 0;
            for(i=0; i<=n-1; i++)
            {
                a.ptr.p_double[i] = i/((n+1)/2);
                c.ptr.p_int[i] = i/((n+1)/2);
                if( c.ptr.p_int[i]==0 )
                {
                    c0 = c0+1;
                }
                if( c.ptr.p_int[i]==1 )
                {
                    c1 = c1+1;
                }
            }
            dssplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                splitkerrors = ae_true;
                continue;
            }
            splitkerrors = splitkerrors||ni!=2;
            if( ni==2 )
            {
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
            }
        }
        
        /*
         * test #2
         */
        if( n>2 )
        {
            c0 = 1+ae_randominteger(n-1, _state);
            c1 = n-c0;
            for(i=0; i<=n-1; i++)
            {
                if( i<c0 )
                {
                    a.ptr.p_double[i] = 0;
                    c.ptr.p_int[i] = 0;
                }
                else
                {
                    a.ptr.p_double[i] = 1;
                    c.ptr.p_int[i] = 1;
                }
            }
            dssplitk(&a, &c, n, 2, 2+ae_randominteger(5, _state), &info, &thresholds, &ni, &cve, _state);
            if( info!=1 )
            {
                splitkerrors = ae_true;
                continue;
            }
            splitkerrors = splitkerrors||ni!=2;
            if( ni==2 )
            {
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[0]-0.5, _state),100*ae_machineepsilon);
                splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(cve-(-c0*ae_log((double)c0/(double)(c0+1), _state)-c1*ae_log((double)c1/(double)(c1+1), _state)), _state),100*ae_machineepsilon);
            }
        }
        
        /*
         * multi-tie test
         */
        for(c0=4; c0<=n; c0++)
        {
            if( (n%c0==0&&n/c0<=c0)&&n/c0>1 )
            {
                nc = n/c0;
                for(i=0; i<=nc-1; i++)
                {
                    for(j=c0*i; j<=c0*(i+1)-1; j++)
                    {
                        a.ptr.p_double[j] = j;
                        c.ptr.p_int[j] = i;
                    }
                }
                dssplitk(&a, &c, n, nc, nc+ae_randominteger(nc, _state), &info, &thresholds, &ni, &cve, _state);
                if( info!=1 )
                {
                    splitkerrors = ae_true;
                    continue;
                }
                splitkerrors = splitkerrors||ni!=nc;
                if( ni==nc )
                {
                    for(i=0; i<=nc-2; i++)
                    {
                        splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(thresholds.ptr.p_double[i]-(c0*(i+1)-1+0.5), _state),100*ae_machineepsilon);
                    }
                    cvr = -nc*c0*ae_log((double)c0/(double)(c0+nc-1), _state);
                    splitkerrors = splitkerrors||ae_fp_greater(ae_fabs(cve-cvr, _state),100*ae_machineepsilon);
                }
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ((tieserrors||split2errors)||optimalsplitkerrors)||splitkerrors;
    if( !silent )
    {
        printf("TESTING BASIC DATASET SUBROUTINES\n");
        printf("TIES:                               ");
        if( !tieserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPLIT-2:                            ");
        if( !split2errors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("OPTIMAL SPLIT-K:                    ");
        if( !optimalsplitkerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPLIT-K:                            ");
        if( !splitkerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testbdssunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testbdssunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testbdssunit_unset1di(/* Integer */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_int[0] = ae_randominteger(3, _state)-1;
}


static void testbdssunit_testsortresults(/* Real    */ ae_vector* asorted,
     /* Integer */ ae_vector* p1,
     /* Integer */ ae_vector* p2,
     /* Real    */ ae_vector* aoriginal,
     ae_int_t n,
     ae_bool* waserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_vector a2;
    double t;
    ae_vector f;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&f, 0, DT_INT, _state, ae_true);

    ae_vector_set_length(&a2, n-1+1, _state);
    ae_vector_set_length(&f, n-1+1, _state);
    
    /*
     * is set ordered?
     */
    for(i=0; i<=n-2; i++)
    {
        *waserrors = *waserrors||ae_fp_greater(asorted->ptr.p_double[i],asorted->ptr.p_double[i+1]);
    }
    
    /*
     * P1 correctness
     */
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||ae_fp_neq(asorted->ptr.p_double[i],aoriginal->ptr.p_double[p1->ptr.p_int[i]]);
    }
    for(i=0; i<=n-1; i++)
    {
        f.ptr.p_int[i] = 0;
    }
    for(i=0; i<=n-1; i++)
    {
        f.ptr.p_int[p1->ptr.p_int[i]] = f.ptr.p_int[p1->ptr.p_int[i]]+1;
    }
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||f.ptr.p_int[i]!=1;
    }
    
    /*
     * P2 correctness
     */
    for(i=0; i<=n-1; i++)
    {
        a2.ptr.p_double[i] = aoriginal->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        if( p2->ptr.p_int[i]!=i )
        {
            t = a2.ptr.p_double[i];
            a2.ptr.p_double[i] = a2.ptr.p_double[p2->ptr.p_int[i]];
            a2.ptr.p_double[p2->ptr.p_int[i]] = t;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        *waserrors = *waserrors||ae_fp_neq(asorted->ptr.p_double[i],a2.ptr.p_double[i]);
    }
    ae_frame_leave(_state);
}



static void testdforestunit_testprocessing(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest1(ae_int_t nvars,
     ae_int_t nclasses,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testdforestunit_basictest2(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest3(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest4(ae_bool* err, ae_state *_state);
static void testdforestunit_basictest5(ae_bool* err, ae_state *_state);
static double testdforestunit_rnormal(ae_state *_state);
static void testdforestunit_rsphere(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t i,
     ae_state *_state);
static void testdforestunit_unsetdf(decisionforest* df, ae_state *_state);





ae_bool testdforest(ae_bool silent, ae_state *_state)
{
    ae_int_t ncmax;
    ae_int_t nvmax;
    ae_int_t passcount;
    ae_int_t nvars;
    ae_int_t nclasses;
    ae_bool waserrors;
    ae_bool basicerrors;
    ae_bool procerrors;
    ae_bool result;


    
    /*
     * Primary settings
     */
    nvmax = 4;
    ncmax = 3;
    passcount = 10;
    basicerrors = ae_false;
    procerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Tests
     */
    testdforestunit_testprocessing(&procerrors, _state);
    for(nvars=1; nvars<=nvmax; nvars++)
    {
        for(nclasses=1; nclasses<=ncmax; nclasses++)
        {
            testdforestunit_basictest1(nvars, nclasses, passcount, &basicerrors, _state);
        }
    }
    testdforestunit_basictest2(&basicerrors, _state);
    testdforestunit_basictest3(&basicerrors, _state);
    testdforestunit_basictest4(&basicerrors, _state);
    testdforestunit_basictest5(&basicerrors, _state);
    
    /*
     * Final report
     */
    waserrors = basicerrors||procerrors;
    if( !silent )
    {
        printf("RANDOM FOREST TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* PROCESSING FUNCTIONS:                  ");
        if( !procerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* BASIC TESTS:                           ");
        if( !basicerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Processing functions test
*************************************************************************/
static void testdforestunit_testprocessing(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t nvars;
    ae_int_t nclasses;
    ae_int_t nsample;
    ae_int_t ntrees;
    ae_int_t nfeatures;
    ae_int_t flags;
    decisionforest df1;
    decisionforest df2;
    ae_int_t npoints;
    ae_matrix xy;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t i;
    ae_int_t j;
    ae_bool allsame;
    ae_int_t info;
    dfreport rep;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    double v;

    ae_frame_make(_state, &_frame_block);
    _decisionforest_init(&df1, _state, ae_true);
    _decisionforest_init(&df2, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);

    passcount = 100;
    
    /*
     * Main cycle
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * initialize parameters
         */
        nvars = 1+ae_randominteger(5, _state);
        nclasses = 1+ae_randominteger(3, _state);
        ntrees = 1+ae_randominteger(4, _state);
        nfeatures = 1+ae_randominteger(nvars, _state);
        flags = 0;
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            flags = flags+2;
        }
        
        /*
         * Initialize arrays and data
         */
        npoints = 10+ae_randominteger(50, _state);
        nsample = ae_maxint(10, ae_randominteger(npoints, _state), _state);
        ae_vector_set_length(&x1, nvars-1+1, _state);
        ae_vector_set_length(&x2, nvars-1+1, _state);
        ae_vector_set_length(&y1, nclasses-1+1, _state);
        ae_vector_set_length(&y2, nclasses-1+1, _state);
        ae_matrix_set_length(&xy, npoints-1+1, nvars+1, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nvars-1; j++)
            {
                if( j%2==0 )
                {
                    xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    xy.ptr.pp_double[i][j] = ae_randominteger(2, _state);
                }
            }
            if( nclasses==1 )
            {
                xy.ptr.pp_double[i][nvars] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                xy.ptr.pp_double[i][nvars] = ae_randominteger(nclasses, _state);
            }
        }
        
        /*
         * create forest
         */
        dfbuildinternal(&xy, npoints, nvars, nclasses, ntrees, nsample, nfeatures, flags, &info, &df1, &rep, _state);
        if( info<=0 )
        {
            *err = ae_true;
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Same inputs leads to same outputs
         */
        for(i=0; i<=nvars-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nclasses-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        dfprocess(&df1, &x1, &y1, _state);
        dfprocess(&df1, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nclasses-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original forest leads to same outputs
         * on copy created using DFCopy
         */
        testdforestunit_unsetdf(&df2, _state);
        dfcopy(&df1, &df2, _state);
        for(i=0; i<=nvars-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nclasses-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        dfprocess(&df1, &x1, &y1, _state);
        dfprocess(&df2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nclasses-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original forest leads to same outputs
         * on copy created using DFSerialize
         */
        testdforestunit_unsetdf(&df2, _state);
        {
            /*
             * This code passes data structure through serializers
             * (serializes it to string and loads back)
             */
            ae_serializer _local_serializer;
            ae_int_t _local_ssize;
            ae_frame _local_frame_block;
            ae_dyn_block _local_dynamic_block;
            
            ae_frame_make(_state, &_local_frame_block);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_alloc_start(&_local_serializer);
            dfalloc(&_local_serializer, &df1, _state);
            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
            ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            dfserialize(&_local_serializer, &df1, _state);
            ae_serializer_stop(&_local_serializer);
            ae_serializer_clear(&_local_serializer);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            dfunserialize(&_local_serializer, &df2, _state);
            ae_serializer_stop(&_local_serializer);
            ae_serializer_clear(&_local_serializer);
            
            ae_frame_leave(_state);
        }
        for(i=0; i<=nvars-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nclasses-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        dfprocess(&df1, &x1, &y1, _state);
        dfprocess(&df2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nclasses-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Normalization properties
         */
        if( nclasses>1 )
        {
            for(i=0; i<=nvars-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            dfprocess(&df1, &x1, &y1, _state);
            v = 0;
            for(i=0; i<=nclasses-1; i++)
            {
                v = v+y1.ptr.p_double[i];
                *err = *err||ae_fp_less(y1.ptr.p_double[i],0);
            }
            *err = *err||ae_fp_greater(ae_fabs(v-1, _state),1000*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test:  one-tree forest built using full sample must remember all the
training cases
*************************************************************************/
static void testdforestunit_basictest1(ae_int_t nvars,
     ae_int_t nclasses,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double s;
    ae_int_t info;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    ae_bool hassame;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);

    if( nclasses==1 )
    {
        
        /*
         * only classification tasks
         */
        ae_frame_leave(_state);
        return;
    }
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * select number of points
         */
        if( pass<=3&&passcount>3 )
        {
            npoints = pass;
        }
        else
        {
            npoints = 100+ae_randominteger(100, _state);
        }
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints-1+1, nvars+1, _state);
        ae_vector_set_length(&x, nvars-1+1, _state);
        ae_vector_set_length(&y, nclasses-1+1, _state);
        for(i=0; i<=npoints-1; i++)
        {
            for(j=0; j<=nvars-1; j++)
            {
                xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            xy.ptr.pp_double[i][nvars] = ae_randominteger(nclasses, _state);
        }
        
        /*
         * Test
         */
        dfbuildinternal(&xy, npoints, nvars, nclasses, 1, npoints, 1, 1, &info, &df, &rep, _state);
        if( info<=0 )
        {
            *err = ae_true;
            ae_frame_leave(_state);
            return;
        }
        for(i=0; i<=npoints-1; i++)
        {
            ae_v_move(&x.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            dfprocess(&df, &x, &y, _state);
            s = 0;
            for(j=0; j<=nclasses-1; j++)
            {
                if( ae_fp_less(y.ptr.p_double[j],0) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                s = s+y.ptr.p_double[j];
            }
            if( ae_fp_greater(ae_fabs(s-1, _state),1000*ae_machineepsilon) )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            if( ae_fp_greater(ae_fabs(y.ptr.p_double[ae_round(xy.ptr.pp_double[i][nvars], _state)]-1, _state),1000*ae_machineepsilon) )
            {
                
                /*
                 * not an error if there exists such K,J that XY[K,J]=XY[I,J]
                 * (may be we just can't distinguish two tied values).
                 *
                 * definitely error otherwise.
                 */
                hassame = ae_false;
                for(k=0; k<=npoints-1; k++)
                {
                    if( k!=i )
                    {
                        for(j=0; j<=nvars-1; j++)
                        {
                            if( ae_fp_eq(xy.ptr.pp_double[k][j],xy.ptr.pp_double[i][j]) )
                            {
                                hassame = ae_true;
                            }
                        }
                    }
                }
                if( !hassame )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test:  tests generalization ability on a simple noisy classification
task:
* 0<x<1 - P(class=0)=1
* 1<x<2 - P(class=0)=2-x
* 2<x<3 - P(class=0)=0
*************************************************************************/
static void testdforestunit_basictest2(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t i;
    ae_int_t j;
    double s;
    ae_int_t info;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);

    passcount = 1;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * select npoints and ntrees
         */
        npoints = 3000;
        ntrees = 50;
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints-1+1, 1+1, _state);
        ae_vector_set_length(&x, 0+1, _state);
        ae_vector_set_length(&y, 1+1, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = 3*ae_randomreal(_state);
            if( ae_fp_less_eq(xy.ptr.pp_double[i][0],1) )
            {
                xy.ptr.pp_double[i][1] = 0;
            }
            else
            {
                if( ae_fp_less_eq(xy.ptr.pp_double[i][0],2) )
                {
                    if( ae_fp_less(ae_randomreal(_state),xy.ptr.pp_double[i][0]-1) )
                    {
                        xy.ptr.pp_double[i][1] = 1;
                    }
                    else
                    {
                        xy.ptr.pp_double[i][1] = 0;
                    }
                }
                else
                {
                    xy.ptr.pp_double[i][1] = 1;
                }
            }
        }
        
        /*
         * Test
         */
        dfbuildinternal(&xy, npoints, 1, 2, ntrees, ae_round(0.05*npoints, _state), 1, 0, &info, &df, &rep, _state);
        if( info<=0 )
        {
            *err = ae_true;
            ae_frame_leave(_state);
            return;
        }
        x.ptr.p_double[0] = 0.0;
        while(ae_fp_less_eq(x.ptr.p_double[0],3.0))
        {
            dfprocess(&df, &x, &y, _state);
            
            /*
             * Test for basic properties
             */
            s = 0;
            for(j=0; j<=1; j++)
            {
                if( ae_fp_less(y.ptr.p_double[j],0) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                s = s+y.ptr.p_double[j];
            }
            if( ae_fp_greater(ae_fabs(s-1, _state),1000*ae_machineepsilon) )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            
            /*
             * test for good correlation with results
             */
            if( ae_fp_less(x.ptr.p_double[0],1) )
            {
                *err = *err||ae_fp_less(y.ptr.p_double[0],0.8);
            }
            if( ae_fp_greater_eq(x.ptr.p_double[0],1)&&ae_fp_less_eq(x.ptr.p_double[0],2) )
            {
                *err = *err||ae_fp_greater(ae_fabs(y.ptr.p_double[1]-(x.ptr.p_double[0]-1), _state),0.5);
            }
            if( ae_fp_greater(x.ptr.p_double[0],2) )
            {
                *err = *err||ae_fp_less(y.ptr.p_double[1],0.8);
            }
            x.ptr.p_double[0] = x.ptr.p_double[0]+0.01;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test:  tests  generalization ability on a simple classification task
(no noise):
* |x|<1, |y|<1
* x^2+y^2<=0.25 - P(class=0)=1
* x^2+y^2>0.25  - P(class=0)=0
*************************************************************************/
static void testdforestunit_basictest3(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double s;
    ae_int_t info;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    ae_int_t testgridsize;
    double r;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);

    passcount = 1;
    testgridsize = 50;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * select npoints and ntrees
         */
        npoints = 2000;
        ntrees = 100;
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints-1+1, 2+1, _state);
        ae_vector_set_length(&x, 1+1, _state);
        ae_vector_set_length(&y, 1+1, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
            xy.ptr.pp_double[i][1] = 2*ae_randomreal(_state)-1;
            if( ae_fp_less_eq(ae_sqr(xy.ptr.pp_double[i][0], _state)+ae_sqr(xy.ptr.pp_double[i][1], _state),0.25) )
            {
                xy.ptr.pp_double[i][2] = 0;
            }
            else
            {
                xy.ptr.pp_double[i][2] = 1;
            }
        }
        
        /*
         * Test
         */
        dfbuildinternal(&xy, npoints, 2, 2, ntrees, ae_round(0.1*npoints, _state), 1, 0, &info, &df, &rep, _state);
        if( info<=0 )
        {
            *err = ae_true;
            ae_frame_leave(_state);
            return;
        }
        for(i=-testgridsize/2; i<=testgridsize/2; i++)
        {
            for(j=-testgridsize/2; j<=testgridsize/2; j++)
            {
                x.ptr.p_double[0] = (double)i/(double)(testgridsize/2);
                x.ptr.p_double[1] = (double)j/(double)(testgridsize/2);
                dfprocess(&df, &x, &y, _state);
                
                /*
                 * Test for basic properties
                 */
                s = 0;
                for(k=0; k<=1; k++)
                {
                    if( ae_fp_less(y.ptr.p_double[k],0) )
                    {
                        *err = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    s = s+y.ptr.p_double[k];
                }
                if( ae_fp_greater(ae_fabs(s-1, _state),1000*ae_machineepsilon) )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * test for good correlation with results
                 */
                r = ae_sqrt(ae_sqr(x.ptr.p_double[0], _state)+ae_sqr(x.ptr.p_double[1], _state), _state);
                if( ae_fp_less(r,0.5*0.5) )
                {
                    *err = *err||ae_fp_less(y.ptr.p_double[0],0.6);
                }
                if( ae_fp_greater(r,0.5*1.5) )
                {
                    *err = *err||ae_fp_less(y.ptr.p_double[1],0.6);
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test: simple regression task without noise:
* |x|<1, |y|<1
* F(x,y) = x^2+y
*************************************************************************/
static void testdforestunit_basictest4(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix xy;
    ae_int_t npoints;
    ae_int_t ntrees;
    ae_int_t ns;
    ae_int_t strongc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t info;
    decisionforest df;
    decisionforest df2;
    ae_vector x;
    ae_vector y;
    dfreport rep;
    dfreport rep2;
    ae_int_t testgridsize;
    double maxerr;
    double maxerr2;
    double avgerr;
    double avgerr2;
    ae_int_t cnt;
    double ey;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    _decisionforest_init(&df2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);
    _dfreport_init(&rep2, _state, ae_true);

    passcount = 1;
    testgridsize = 50;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * select npoints and ntrees
         */
        npoints = 5000;
        ntrees = 100;
        ns = ae_round(0.1*npoints, _state);
        strongc = 1;
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&xy, npoints-1+1, 2+1, _state);
        ae_vector_set_length(&x, 1+1, _state);
        ae_vector_set_length(&y, 0+1, _state);
        for(i=0; i<=npoints-1; i++)
        {
            xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
            xy.ptr.pp_double[i][1] = 2*ae_randomreal(_state)-1;
            xy.ptr.pp_double[i][2] = ae_sqr(xy.ptr.pp_double[i][0], _state)+xy.ptr.pp_double[i][1];
        }
        
        /*
         * Test
         */
        dfbuildinternal(&xy, npoints, 2, 1, ntrees, ns, 1, 0, &info, &df, &rep, _state);
        if( info<=0 )
        {
            *err = ae_true;
            ae_frame_leave(_state);
            return;
        }
        dfbuildinternal(&xy, npoints, 2, 1, ntrees, ns, 1, strongc, &info, &df2, &rep2, _state);
        if( info<=0 )
        {
            *err = ae_true;
            ae_frame_leave(_state);
            return;
        }
        maxerr = 0;
        maxerr2 = 0;
        avgerr = 0;
        avgerr2 = 0;
        cnt = 0;
        for(i=ae_round(-0.7*testgridsize/2, _state); i<=ae_round(0.7*testgridsize/2, _state); i++)
        {
            for(j=ae_round(-0.7*testgridsize/2, _state); j<=ae_round(0.7*testgridsize/2, _state); j++)
            {
                x.ptr.p_double[0] = (double)i/(double)(testgridsize/2);
                x.ptr.p_double[1] = (double)j/(double)(testgridsize/2);
                ey = ae_sqr(x.ptr.p_double[0], _state)+x.ptr.p_double[1];
                dfprocess(&df, &x, &y, _state);
                maxerr = ae_maxreal(maxerr, ae_fabs(y.ptr.p_double[0]-ey, _state), _state);
                avgerr = avgerr+ae_fabs(y.ptr.p_double[0]-ey, _state);
                dfprocess(&df2, &x, &y, _state);
                maxerr2 = ae_maxreal(maxerr2, ae_fabs(y.ptr.p_double[0]-ey, _state), _state);
                avgerr2 = avgerr2+ae_fabs(y.ptr.p_double[0]-ey, _state);
                cnt = cnt+1;
            }
        }
        avgerr = avgerr/cnt;
        avgerr2 = avgerr2/cnt;
        *err = *err||ae_fp_greater(maxerr,0.2);
        *err = *err||ae_fp_greater(maxerr2,0.2);
        *err = *err||ae_fp_greater(avgerr,0.1);
        *err = *err||ae_fp_greater(avgerr2,0.1);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Basic test: extended variable selection leads to better results.

Next task CAN be solved without EVS but it is very unlikely. With EVS
it can be easily and exactly solved.

Task matrix:
    1 0 0 0 ... 0   0
    0 1 0 0 ... 0   1
    0 0 1 0 ... 0   2
    0 0 0 1 ... 0   3
    0 0 0 0 ... 1   N-1
*************************************************************************/
static void testdforestunit_basictest5(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t nvars;
    ae_int_t npoints;
    ae_int_t nfeatures;
    ae_int_t nsample;
    ae_int_t ntrees;
    ae_int_t evs;
    ae_int_t i;
    ae_int_t j;
    ae_bool eflag;
    ae_int_t info;
    decisionforest df;
    ae_vector x;
    ae_vector y;
    dfreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _decisionforest_init(&df, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);

    
    /*
     * select npoints and ntrees
     */
    npoints = 50;
    nvars = npoints;
    ntrees = 1;
    nsample = npoints;
    evs = 2;
    nfeatures = 1;
    
    /*
     * Prepare task
     */
    ae_matrix_set_length(&xy, npoints-1+1, nvars+1, _state);
    ae_vector_set_length(&x, nvars-1+1, _state);
    ae_vector_set_length(&y, 0+1, _state);
    for(i=0; i<=npoints-1; i++)
    {
        for(j=0; j<=nvars-1; j++)
        {
            xy.ptr.pp_double[i][j] = 0;
        }
        xy.ptr.pp_double[i][i] = 1;
        xy.ptr.pp_double[i][nvars] = i;
    }
    
    /*
     * Without EVS
     */
    dfbuildinternal(&xy, npoints, nvars, 1, ntrees, nsample, nfeatures, 0, &info, &df, &rep, _state);
    if( info<=0 )
    {
        *err = ae_true;
        ae_frame_leave(_state);
        return;
    }
    eflag = ae_false;
    for(i=0; i<=npoints-1; i++)
    {
        ae_v_move(&x.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
        dfprocess(&df, &x, &y, _state);
        if( ae_fp_greater(ae_fabs(y.ptr.p_double[0]-xy.ptr.pp_double[i][nvars], _state),1000*ae_machineepsilon) )
        {
            eflag = ae_true;
        }
    }
    if( !eflag )
    {
        *err = ae_true;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * With EVS
     */
    dfbuildinternal(&xy, npoints, nvars, 1, ntrees, nsample, nfeatures, evs, &info, &df, &rep, _state);
    if( info<=0 )
    {
        *err = ae_true;
        ae_frame_leave(_state);
        return;
    }
    eflag = ae_false;
    for(i=0; i<=npoints-1; i++)
    {
        ae_v_move(&x.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
        dfprocess(&df, &x, &y, _state);
        if( ae_fp_greater(ae_fabs(y.ptr.p_double[0]-xy.ptr.pp_double[i][nvars], _state),1000*ae_machineepsilon) )
        {
            eflag = ae_true;
        }
    }
    if( eflag )
    {
        *err = ae_true;
        ae_frame_leave(_state);
        return;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Random normal number
*************************************************************************/
static double testdforestunit_rnormal(ae_state *_state)
{
    double u;
    double v;
    double s;
    double x1;
    double x2;
    double result;


    for(;;)
    {
        u = 2*ae_randomreal(_state)-1;
        v = 2*ae_randomreal(_state)-1;
        s = ae_sqr(u, _state)+ae_sqr(v, _state);
        if( ae_fp_greater(s,0)&&ae_fp_less(s,1) )
        {
            s = ae_sqrt(-2*ae_log(s, _state)/s, _state);
            x1 = u*s;
            x2 = v*s;
            break;
        }
    }
    result = x1;
    return result;
}


/*************************************************************************
Random point from sphere
*************************************************************************/
static void testdforestunit_rsphere(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t j;
    double v;


    for(j=0; j<=n-1; j++)
    {
        xy->ptr.pp_double[i][j] = testdforestunit_rnormal(_state);
    }
    v = ae_v_dotproduct(&xy->ptr.pp_double[i][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    v = ae_randomreal(_state)/ae_sqrt(v, _state);
    ae_v_muld(&xy->ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
}


/*************************************************************************
Unsets DF
*************************************************************************/
static void testdforestunit_unsetdf(decisionforest* df, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t info;
    dfreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _dfreport_init(&rep, _state, ae_true);

    ae_matrix_set_length(&xy, 0+1, 1+1, _state);
    xy.ptr.pp_double[0][0] = 0;
    xy.ptr.pp_double[0][1] = 0;
    dfbuildinternal(&xy, 1, 1, 1, 1, 1, 1, 0, &info, df, &rep, _state);
    ae_frame_leave(_state);
}



static void testblasunit_naivematrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     double alpha,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     double beta,
     ae_state *_state);





ae_bool testblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t i;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j;
    ae_int_t j1;
    ae_int_t j2;
    ae_int_t l;
    ae_int_t k;
    ae_int_t r;
    ae_int_t i3;
    ae_int_t j3;
    ae_int_t col1;
    ae_int_t col2;
    ae_int_t row1;
    ae_int_t row2;
    ae_vector x1;
    ae_vector x2;
    ae_matrix a;
    ae_matrix b;
    ae_matrix c1;
    ae_matrix c2;
    double err;
    double e1;
    double e2;
    double e3;
    double v;
    double scl1;
    double scl2;
    double scl3;
    ae_bool was1;
    ae_bool was2;
    ae_bool trans1;
    ae_bool trans2;
    double threshold;
    ae_bool n2errors;
    ae_bool hsnerrors;
    ae_bool amaxerrors;
    ae_bool mverrors;
    ae_bool iterrors;
    ae_bool cterrors;
    ae_bool mmerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c2, 0, 0, DT_REAL, _state, ae_true);

    n2errors = ae_false;
    amaxerrors = ae_false;
    hsnerrors = ae_false;
    mverrors = ae_false;
    iterrors = ae_false;
    cterrors = ae_false;
    mmerrors = ae_false;
    waserrors = ae_false;
    threshold = 10000*ae_machineepsilon;
    
    /*
     * Test Norm2
     */
    passcount = 1000;
    e1 = 0;
    e2 = 0;
    e3 = 0;
    scl2 = 0.5*ae_maxrealnumber;
    scl3 = 2*ae_minrealnumber;
    for(pass=1; pass<=passcount; pass++)
    {
        n = 1+ae_randominteger(1000, _state);
        i1 = ae_randominteger(10, _state);
        i2 = n+i1-1;
        ae_vector_set_length(&x1, i2+1, _state);
        ae_vector_set_length(&x2, i2+1, _state);
        for(i=i1; i<=i2; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        v = 0;
        for(i=i1; i<=i2; i++)
        {
            v = v+ae_sqr(x1.ptr.p_double[i], _state);
        }
        v = ae_sqrt(v, _state);
        e1 = ae_maxreal(e1, ae_fabs(v-vectornorm2(&x1, i1, i2, _state), _state), _state);
        for(i=i1; i<=i2; i++)
        {
            x2.ptr.p_double[i] = scl2*x1.ptr.p_double[i];
        }
        e2 = ae_maxreal(e2, ae_fabs(v*scl2-vectornorm2(&x2, i1, i2, _state), _state), _state);
        for(i=i1; i<=i2; i++)
        {
            x2.ptr.p_double[i] = scl3*x1.ptr.p_double[i];
        }
        e3 = ae_maxreal(e3, ae_fabs(v*scl3-vectornorm2(&x2, i1, i2, _state), _state), _state);
    }
    e2 = e2/scl2;
    e3 = e3/scl3;
    n2errors = (ae_fp_greater_eq(e1,threshold)||ae_fp_greater_eq(e2,threshold))||ae_fp_greater_eq(e3,threshold);
    
    /*
     * Testing VectorAbsMax, Column/Row AbsMax
     */
    ae_vector_set_length(&x1, 5+1, _state);
    x1.ptr.p_double[1] = 2.0;
    x1.ptr.p_double[2] = 0.2;
    x1.ptr.p_double[3] = -1.3;
    x1.ptr.p_double[4] = 0.7;
    x1.ptr.p_double[5] = -3.0;
    amaxerrors = (vectoridxabsmax(&x1, 1, 5, _state)!=5||vectoridxabsmax(&x1, 1, 4, _state)!=1)||vectoridxabsmax(&x1, 2, 4, _state)!=3;
    n = 30;
    ae_vector_set_length(&x1, n+1, _state);
    ae_matrix_set_length(&a, n+1, n+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
        }
    }
    was1 = ae_false;
    was2 = ae_false;
    for(pass=1; pass<=1000; pass++)
    {
        j = 1+ae_randominteger(n, _state);
        i1 = 1+ae_randominteger(n, _state);
        i2 = i1+ae_randominteger(n+1-i1, _state);
        ae_v_move(&x1.ptr.p_double[i1], 1, &a.ptr.pp_double[i1][j], a.stride, ae_v_len(i1,i2));
        if( vectoridxabsmax(&x1, i1, i2, _state)!=columnidxabsmax(&a, i1, i2, j, _state) )
        {
            was1 = ae_true;
        }
        i = 1+ae_randominteger(n, _state);
        j1 = 1+ae_randominteger(n, _state);
        j2 = j1+ae_randominteger(n+1-j1, _state);
        ae_v_move(&x1.ptr.p_double[j1], 1, &a.ptr.pp_double[i][j1], 1, ae_v_len(j1,j2));
        if( vectoridxabsmax(&x1, j1, j2, _state)!=rowidxabsmax(&a, j1, j2, i, _state) )
        {
            was2 = ae_true;
        }
    }
    amaxerrors = (amaxerrors||was1)||was2;
    
    /*
     * Testing upper Hessenberg 1-norm
     */
    ae_matrix_set_length(&a, 3+1, 3+1, _state);
    ae_vector_set_length(&x1, 3+1, _state);
    a.ptr.pp_double[1][1] = 2;
    a.ptr.pp_double[1][2] = 3;
    a.ptr.pp_double[1][3] = 1;
    a.ptr.pp_double[2][1] = 4;
    a.ptr.pp_double[2][2] = -5;
    a.ptr.pp_double[2][3] = 8;
    a.ptr.pp_double[3][1] = 99;
    a.ptr.pp_double[3][2] = 3;
    a.ptr.pp_double[3][3] = 1;
    hsnerrors = ae_fp_greater(ae_fabs(upperhessenberg1norm(&a, 1, 3, 1, 3, &x1, _state)-11, _state),threshold);
    
    /*
     * Testing MatrixVectorMultiply
     */
    ae_matrix_set_length(&a, 3+1, 5+1, _state);
    ae_vector_set_length(&x1, 3+1, _state);
    ae_vector_set_length(&x2, 2+1, _state);
    a.ptr.pp_double[2][3] = 2;
    a.ptr.pp_double[2][4] = -1;
    a.ptr.pp_double[2][5] = -1;
    a.ptr.pp_double[3][3] = 1;
    a.ptr.pp_double[3][4] = -2;
    a.ptr.pp_double[3][5] = 2;
    x1.ptr.p_double[1] = 1;
    x1.ptr.p_double[2] = 2;
    x1.ptr.p_double[3] = 1;
    x2.ptr.p_double[1] = -1;
    x2.ptr.p_double[2] = -1;
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_false, &x1, 1, 3, 1.0, &x2, 1, 2, 1.0, _state);
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_true, &x2, 1, 2, 1.0, &x1, 1, 3, 1.0, _state);
    e1 = ae_fabs(x1.ptr.p_double[1]+5, _state)+ae_fabs(x1.ptr.p_double[2]-8, _state)+ae_fabs(x1.ptr.p_double[3]+1, _state)+ae_fabs(x2.ptr.p_double[1]+2, _state)+ae_fabs(x2.ptr.p_double[2]+2, _state);
    x1.ptr.p_double[1] = 1;
    x1.ptr.p_double[2] = 2;
    x1.ptr.p_double[3] = 1;
    x2.ptr.p_double[1] = -1;
    x2.ptr.p_double[2] = -1;
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_false, &x1, 1, 3, 1.0, &x2, 1, 2, 0.0, _state);
    matrixvectormultiply(&a, 2, 3, 3, 5, ae_true, &x2, 1, 2, 1.0, &x1, 1, 3, 0.0, _state);
    e2 = ae_fabs(x1.ptr.p_double[1]+3, _state)+ae_fabs(x1.ptr.p_double[2]-3, _state)+ae_fabs(x1.ptr.p_double[3]+1, _state)+ae_fabs(x2.ptr.p_double[1]+1, _state)+ae_fabs(x2.ptr.p_double[2]+1, _state);
    mverrors = ae_fp_greater_eq(e1+e2,threshold);
    
    /*
     * testing inplace transpose
     */
    n = 10;
    ae_matrix_set_length(&a, n+1, n+1, _state);
    ae_matrix_set_length(&b, n+1, n+1, _state);
    ae_vector_set_length(&x1, n-1+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a.ptr.pp_double[i][j] = ae_randomreal(_state);
        }
    }
    passcount = 10000;
    was1 = ae_false;
    for(pass=1; pass<=passcount; pass++)
    {
        i1 = 1+ae_randominteger(n, _state);
        i2 = i1+ae_randominteger(n-i1+1, _state);
        j1 = 1+ae_randominteger(n-(i2-i1), _state);
        j2 = j1+(i2-i1);
        copymatrix(&a, i1, i2, j1, j2, &b, i1, i2, j1, j2, _state);
        inplacetranspose(&b, i1, i2, j1, j2, &x1, _state);
        for(i=i1; i<=i2; i++)
        {
            for(j=j1; j<=j2; j++)
            {
                if( ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[i1+(j-j1)][j1+(i-i1)]) )
                {
                    was1 = ae_true;
                }
            }
        }
    }
    iterrors = was1;
    
    /*
     * testing copy and transpose
     */
    n = 10;
    ae_matrix_set_length(&a, n+1, n+1, _state);
    ae_matrix_set_length(&b, n+1, n+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a.ptr.pp_double[i][j] = ae_randomreal(_state);
        }
    }
    passcount = 10000;
    was1 = ae_false;
    for(pass=1; pass<=passcount; pass++)
    {
        i1 = 1+ae_randominteger(n, _state);
        i2 = i1+ae_randominteger(n-i1+1, _state);
        j1 = 1+ae_randominteger(n, _state);
        j2 = j1+ae_randominteger(n-j1+1, _state);
        copyandtranspose(&a, i1, i2, j1, j2, &b, j1, j2, i1, i2, _state);
        for(i=i1; i<=i2; i++)
        {
            for(j=j1; j<=j2; j++)
            {
                if( ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[j][i]) )
                {
                    was1 = ae_true;
                }
            }
        }
    }
    cterrors = was1;
    
    /*
     * Testing MatrixMatrixMultiply
     */
    n = 10;
    ae_matrix_set_length(&a, 2*n+1, 2*n+1, _state);
    ae_matrix_set_length(&b, 2*n+1, 2*n+1, _state);
    ae_matrix_set_length(&c1, 2*n+1, 2*n+1, _state);
    ae_matrix_set_length(&c2, 2*n+1, 2*n+1, _state);
    ae_vector_set_length(&x1, n+1, _state);
    ae_vector_set_length(&x2, n+1, _state);
    for(i=1; i<=2*n; i++)
    {
        for(j=1; j<=2*n; j++)
        {
            a.ptr.pp_double[i][j] = ae_randomreal(_state);
            b.ptr.pp_double[i][j] = ae_randomreal(_state);
        }
    }
    passcount = 1000;
    was1 = ae_false;
    for(pass=1; pass<=passcount; pass++)
    {
        for(i=1; i<=2*n; i++)
        {
            for(j=1; j<=2*n; j++)
            {
                c1.ptr.pp_double[i][j] = 2.1*i+3.1*j;
                c2.ptr.pp_double[i][j] = c1.ptr.pp_double[i][j];
            }
        }
        l = 1+ae_randominteger(n, _state);
        k = 1+ae_randominteger(n, _state);
        r = 1+ae_randominteger(n, _state);
        i1 = 1+ae_randominteger(n, _state);
        j1 = 1+ae_randominteger(n, _state);
        i2 = 1+ae_randominteger(n, _state);
        j2 = 1+ae_randominteger(n, _state);
        i3 = 1+ae_randominteger(n, _state);
        j3 = 1+ae_randominteger(n, _state);
        trans1 = ae_fp_greater(ae_randomreal(_state),0.5);
        trans2 = ae_fp_greater(ae_randomreal(_state),0.5);
        if( trans1 )
        {
            col1 = l;
            row1 = k;
        }
        else
        {
            col1 = k;
            row1 = l;
        }
        if( trans2 )
        {
            col2 = k;
            row2 = r;
        }
        else
        {
            col2 = r;
            row2 = k;
        }
        scl1 = ae_randomreal(_state);
        scl2 = ae_randomreal(_state);
        matrixmatrixmultiply(&a, i1, i1+row1-1, j1, j1+col1-1, trans1, &b, i2, i2+row2-1, j2, j2+col2-1, trans2, scl1, &c1, i3, i3+l-1, j3, j3+r-1, scl2, &x1, _state);
        testblasunit_naivematrixmatrixmultiply(&a, i1, i1+row1-1, j1, j1+col1-1, trans1, &b, i2, i2+row2-1, j2, j2+col2-1, trans2, scl1, &c2, i3, i3+l-1, j3, j3+r-1, scl2, _state);
        err = 0;
        for(i=1; i<=l; i++)
        {
            for(j=1; j<=r; j++)
            {
                err = ae_maxreal(err, ae_fabs(c1.ptr.pp_double[i3+i-1][j3+j-1]-c2.ptr.pp_double[i3+i-1][j3+j-1], _state), _state);
            }
        }
        if( ae_fp_greater(err,threshold) )
        {
            was1 = ae_true;
            break;
        }
    }
    mmerrors = was1;
    
    /*
     * report
     */
    waserrors = (((((n2errors||amaxerrors)||hsnerrors)||mverrors)||iterrors)||cterrors)||mmerrors;
    if( !silent )
    {
        printf("TESTING BLAS\n");
        printf("VectorNorm2:                             ");
        if( n2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("AbsMax (vector/row/column):              ");
        if( amaxerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("UpperHessenberg1Norm:                    ");
        if( hsnerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("MatrixVectorMultiply:                    ");
        if( mverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("InplaceTranspose:                        ");
        if( iterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CopyAndTranspose:                        ");
        if( cterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("MatrixMatrixMultiply:                    ");
        if( mmerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testblasunit_naivematrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     double alpha,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     double beta,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t arows;
    ae_int_t acols;
    ae_int_t brows;
    ae_int_t bcols;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t r;
    double v;
    ae_vector x1;
    ae_vector x2;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Setup
     */
    if( !transa )
    {
        arows = ai2-ai1+1;
        acols = aj2-aj1+1;
    }
    else
    {
        arows = aj2-aj1+1;
        acols = ai2-ai1+1;
    }
    if( !transb )
    {
        brows = bi2-bi1+1;
        bcols = bj2-bj1+1;
    }
    else
    {
        brows = bj2-bj1+1;
        bcols = bi2-bi1+1;
    }
    ae_assert(acols==brows, "NaiveMatrixMatrixMultiply: incorrect matrix sizes!", _state);
    if( ((arows<=0||acols<=0)||brows<=0)||bcols<=0 )
    {
        ae_frame_leave(_state);
        return;
    }
    l = arows;
    r = bcols;
    k = acols;
    ae_vector_set_length(&x1, k+1, _state);
    ae_vector_set_length(&x2, k+1, _state);
    for(i=1; i<=l; i++)
    {
        for(j=1; j<=r; j++)
        {
            if( !transa )
            {
                if( !transb )
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1][bj1+j-1], b->stride, &a->ptr.pp_double[ai1+i-1][aj1], 1, ae_v_len(bi1,bi2));
                }
                else
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1+j-1][bj1], 1, &a->ptr.pp_double[ai1+i-1][aj1], 1, ae_v_len(bj1,bj2));
                }
            }
            else
            {
                if( !transb )
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1][bj1+j-1], b->stride, &a->ptr.pp_double[ai1][aj1+i-1], a->stride, ae_v_len(bi1,bi2));
                }
                else
                {
                    v = ae_v_dotproduct(&b->ptr.pp_double[bi1+j-1][bj1], 1, &a->ptr.pp_double[ai1][aj1+i-1], a->stride, ae_v_len(bj1,bj2));
                }
            }
            if( ae_fp_eq(beta,0) )
            {
                c->ptr.pp_double[ci1+i-1][cj1+j-1] = alpha*v;
            }
            else
            {
                c->ptr.pp_double[ci1+i-1][cj1+j-1] = beta*c->ptr.pp_double[ci1+i-1][cj1+j-1]+alpha*v;
            }
        }
    }
    ae_frame_leave(_state);
}



static void testkmeansunit_simpletest1(ae_int_t nvars,
     ae_int_t nc,
     ae_int_t passcount,
     ae_bool* converrors,
     ae_bool* othererrors,
     ae_bool* simpleerrors,
     ae_state *_state);
static void testkmeansunit_restartstest(ae_bool* converrors,
     ae_bool* restartserrors,
     ae_state *_state);
static double testkmeansunit_rnormal(ae_state *_state);
static void testkmeansunit_rsphere(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t i,
     ae_state *_state);





ae_bool testkmeans(ae_bool silent, ae_state *_state)
{
    ae_int_t nf;
    ae_int_t maxnf;
    ae_int_t nc;
    ae_int_t maxnc;
    ae_int_t passcount;
    ae_bool waserrors;
    ae_bool converrors;
    ae_bool simpleerrors;
    ae_bool complexerrors;
    ae_bool othererrors;
    ae_bool restartserrors;
    ae_bool result;


    
    /*
     * Primary settings
     */
    maxnf = 5;
    maxnc = 5;
    passcount = 10;
    waserrors = ae_false;
    converrors = ae_false;
    othererrors = ae_false;
    simpleerrors = ae_false;
    complexerrors = ae_false;
    restartserrors = ae_false;
    
    /*
     *
     */
    for(nf=1; nf<=maxnf; nf++)
    {
        for(nc=1; nc<=maxnc; nc++)
        {
            testkmeansunit_simpletest1(nf, nc, passcount, &converrors, &othererrors, &simpleerrors, _state);
        }
    }
    testkmeansunit_restartstest(&converrors, &restartserrors, _state);
    
    /*
     * Final report
     */
    waserrors = (((converrors||othererrors)||simpleerrors)||complexerrors)||restartserrors;
    if( !silent )
    {
        printf("K-MEANS TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* CONVERGENCE:                           ");
        if( !converrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* SIMPLE TASKS:                          ");
        if( !simpleerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* COMPLEX TASKS:                         ");
        if( !complexerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OTHER PROPERTIES:                      ");
        if( !othererrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* RESTARTS PROPERTIES:                   ");
        if( !restartserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Simple test 1: ellipsoid in NF-dimensional space.
compare k-means centers with random centers
*************************************************************************/
static void testkmeansunit_simpletest1(ae_int_t nvars,
     ae_int_t nc,
     ae_int_t passcount,
     ae_bool* converrors,
     ae_bool* othererrors,
     ae_bool* simpleerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t npoints;
    ae_int_t majoraxis;
    ae_matrix xy;
    ae_vector tmp;
    double v;
    ae_int_t i;
    ae_int_t j;
    ae_int_t info;
    ae_matrix c;
    ae_vector xyc;
    ae_int_t pass;
    ae_int_t restarts;
    double ekmeans;
    double erandom;
    double dclosest;
    ae_int_t cclosest;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xyc, 0, DT_INT, _state, ae_true);

    npoints = nc*100;
    restarts = 5;
    passcount = 10;
    ae_vector_set_length(&tmp, nvars-1+1, _state);
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Fill
         */
        ae_matrix_set_length(&xy, npoints-1+1, nvars-1+1, _state);
        majoraxis = ae_randominteger(nvars, _state);
        for(i=0; i<=npoints-1; i++)
        {
            testkmeansunit_rsphere(&xy, nvars, i, _state);
            xy.ptr.pp_double[i][majoraxis] = nc*xy.ptr.pp_double[i][majoraxis];
        }
        
        /*
         * Test
         */
        kmeansgenerate(&xy, npoints, nvars, nc, restarts, &info, &c, &xyc, _state);
        if( info<0 )
        {
            *converrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Test that XYC is correct mapping to cluster centers
         */
        for(i=0; i<=npoints-1; i++)
        {
            cclosest = -1;
            dclosest = ae_maxrealnumber;
            for(j=0; j<=nc-1; j++)
            {
                ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
                ae_v_sub(&tmp.ptr.p_double[0], 1, &c.ptr.pp_double[0][j], c.stride, ae_v_len(0,nvars-1));
                v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
                if( ae_fp_less(v,dclosest) )
                {
                    cclosest = j;
                    dclosest = v;
                }
            }
            if( cclosest!=xyc.ptr.p_int[i] )
            {
                *othererrors = ae_true;
                ae_frame_leave(_state);
                return;
            }
        }
        
        /*
         * Use first NC rows of XY as random centers
         * (XY is totally random, so it is as good as any other choice).
         *
         * Compare potential functions.
         */
        ekmeans = 0;
        for(i=0; i<=npoints-1; i++)
        {
            ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            ae_v_sub(&tmp.ptr.p_double[0], 1, &c.ptr.pp_double[0][xyc.ptr.p_int[i]], c.stride, ae_v_len(0,nvars-1));
            v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
            ekmeans = ekmeans+v;
        }
        erandom = 0;
        for(i=0; i<=npoints-1; i++)
        {
            dclosest = ae_maxrealnumber;
            v = 0;
            for(j=0; j<=nc-1; j++)
            {
                ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
                ae_v_sub(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[j][0], 1, ae_v_len(0,nvars-1));
                v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
                if( ae_fp_less(v,dclosest) )
                {
                    dclosest = v;
                }
            }
            erandom = erandom+v;
        }
        if( ae_fp_less(erandom,ekmeans) )
        {
            *simpleerrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This non-deterministic test checks that Restarts>1 significantly  improves
quality of results.

Subroutine generates random task 3 unit balls in 2D, each with 20  points,
separated by 5 units wide gaps, and solves it  with  Restarts=1  and  with
Restarts=5. Potential functions are compared,  outcome  of  the  trial  is
either 0 or 1 (depending on what is better).

Sequence of 1000 such tasks is  solved.  If  Restarts>1  actually  improve
quality of solution, sum of outcome will be non-binomial.  If  it  doesn't
matter, it will be binomially distributed.

P.S. This test was added after report from Gianluca  Borello  who  noticed
error in the handling of multiple restarts.
*************************************************************************/
static void testkmeansunit_restartstest(ae_bool* converrors,
     ae_bool* restartserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t npoints;
    ae_int_t nvars;
    ae_int_t nclusters;
    ae_int_t clustersize;
    ae_int_t restarts;
    ae_int_t passcount;
    double sigmathreshold;
    double p;
    double s;
    ae_matrix xy;
    ae_matrix ca;
    ae_matrix cb;
    ae_vector xyca;
    ae_vector xycb;
    ae_vector tmp;
    ae_int_t i;
    ae_int_t j;
    ae_int_t info;
    ae_int_t pass;
    double ea;
    double eb;
    double v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cb, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xyca, 0, DT_INT, _state, ae_true);
    ae_vector_init(&xycb, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    restarts = 5;
    passcount = 1000;
    clustersize = 20;
    nclusters = 3;
    nvars = 2;
    npoints = nclusters*clustersize;
    sigmathreshold = 5;
    ae_matrix_set_length(&xy, npoints, nvars, _state);
    ae_vector_set_length(&tmp, nvars, _state);
    p = 0;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Fill
         */
        for(i=0; i<=npoints-1; i++)
        {
            testkmeansunit_rsphere(&xy, nvars, i, _state);
            for(j=0; j<=nvars-1; j++)
            {
                xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]+(double)i/(double)clustersize*5;
            }
        }
        
        /*
         * Test: Restarts=1
         */
        kmeansgenerate(&xy, npoints, nvars, nclusters, 1, &info, &ca, &xyca, _state);
        if( info<0 )
        {
            *converrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        ea = 0;
        for(i=0; i<=npoints-1; i++)
        {
            ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            ae_v_sub(&tmp.ptr.p_double[0], 1, &ca.ptr.pp_double[0][xyca.ptr.p_int[i]], ca.stride, ae_v_len(0,nvars-1));
            v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
            ea = ea+v;
        }
        
        /*
         * Test: Restarts>1
         */
        kmeansgenerate(&xy, npoints, nvars, nclusters, restarts, &info, &cb, &xycb, _state);
        if( info<0 )
        {
            *converrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
        eb = 0;
        for(i=0; i<=npoints-1; i++)
        {
            ae_v_move(&tmp.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nvars-1));
            ae_v_sub(&tmp.ptr.p_double[0], 1, &cb.ptr.pp_double[0][xycb.ptr.p_int[i]], cb.stride, ae_v_len(0,nvars-1));
            v = ae_v_dotproduct(&tmp.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,nvars-1));
            eb = eb+v;
        }
        
        /*
         * Calculate statistic.
         */
        if( ae_fp_less(ea,eb) )
        {
            p = p+1;
        }
        if( ae_fp_eq(ea,eb) )
        {
            p = p+0.5;
        }
    }
    
    /*
     * If Restarts doesn't influence quality of centers found, P must be
     * binomially distributed random value with mean 0.5*PassCount and
     * standard deviation Sqrt(PassCount/4).
     *
     * If Restarts do influence quality of solution, P must be significantly
     * lower than 0.5*PassCount.
     */
    s = (p-0.5*passcount)/ae_sqrt((double)passcount/(double)4, _state);
    *restartserrors = *restartserrors||ae_fp_greater(s,-sigmathreshold);
    ae_frame_leave(_state);
}


/*************************************************************************
Random normal number
*************************************************************************/
static double testkmeansunit_rnormal(ae_state *_state)
{
    double u;
    double v;
    double s;
    double x1;
    double x2;
    double result;


    for(;;)
    {
        u = 2*ae_randomreal(_state)-1;
        v = 2*ae_randomreal(_state)-1;
        s = ae_sqr(u, _state)+ae_sqr(v, _state);
        if( ae_fp_greater(s,0)&&ae_fp_less(s,1) )
        {
            s = ae_sqrt(-2*ae_log(s, _state)/s, _state);
            x1 = u*s;
            x2 = v*s;
            break;
        }
    }
    result = x1;
    return result;
}


/*************************************************************************
Random point from sphere
*************************************************************************/
static void testkmeansunit_rsphere(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t i,
     ae_state *_state)
{
    ae_int_t j;
    double v;


    for(j=0; j<=n-1; j++)
    {
        xy->ptr.pp_double[i][j] = testkmeansunit_rnormal(_state);
    }
    v = ae_v_dotproduct(&xy->ptr.pp_double[i][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    v = ae_randomreal(_state)/ae_sqrt(v, _state);
    ae_v_muld(&xy->ptr.pp_double[i][0], 1, ae_v_len(0,n-1), v);
}








ae_bool testhblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ua;
    ae_matrix la;
    ae_vector x;
    ae_vector y1;
    ae_vector y2;
    ae_vector y3;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t i2;
    ae_bool waserrors;
    double mverr;
    double threshold;
    ae_complex alpha;
    ae_complex v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&x, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y2, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y3, 0, DT_COMPLEX, _state, ae_true);

    mverr = 0;
    waserrors = ae_false;
    maxn = 10;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Test MV
     */
    for(n=2; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n+1, n+1, _state);
        ae_matrix_set_length(&ua, n+1, n+1, _state);
        ae_matrix_set_length(&la, n+1, n+1, _state);
        ae_vector_set_length(&x, n+1, _state);
        ae_vector_set_length(&y1, n+1, _state);
        ae_vector_set_length(&y2, n+1, _state);
        ae_vector_set_length(&y3, n+1, _state);
        
        /*
         * fill A, UA, LA
         */
        for(i=1; i<=n; i++)
        {
            a.ptr.pp_complex[i][i].x = 2*ae_randomreal(_state)-1;
            a.ptr.pp_complex[i][i].y = 0;
            for(j=i+1; j<=n; j++)
            {
                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[j][i] = ae_c_conj(a.ptr.pp_complex[i][j], _state);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                ua.ptr.pp_complex[i][j] = ae_complex_from_d(0);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=i; j<=n; j++)
            {
                ua.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                la.ptr.pp_complex[i][j] = ae_complex_from_d(0);
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=i; j++)
            {
                la.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        
        /*
         * test on different I1, I2
         */
        for(i1=1; i1<=n; i1++)
        {
            for(i2=i1; i2<=n; i2++)
            {
                
                /*
                 * Fill X, choose Alpha
                 */
                for(i=1; i<=i2-i1+1; i++)
                {
                    x.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                    x.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                }
                alpha.x = 2*ae_randomreal(_state)-1;
                alpha.y = 2*ae_randomreal(_state)-1;
                
                /*
                 * calculate A*x, UA*x, LA*x
                 */
                for(i=i1; i<=i2; i++)
                {
                    v = ae_v_cdotproduct(&a.ptr.pp_complex[i][i1], 1, "N", &x.ptr.p_complex[1], 1, "N", ae_v_len(i1,i2));
                    y1.ptr.p_complex[i-i1+1] = ae_c_mul(alpha,v);
                }
                hermitianmatrixvectormultiply(&ua, ae_true, i1, i2, &x, alpha, &y2, _state);
                hermitianmatrixvectormultiply(&la, ae_false, i1, i2, &x, alpha, &y3, _state);
                
                /*
                 * Calculate error
                 */
                ae_v_csub(&y2.ptr.p_complex[1], 1, &y1.ptr.p_complex[1], 1, "N", ae_v_len(1,i2-i1+1));
                v = ae_v_cdotproduct(&y2.ptr.p_complex[1], 1, "N", &y2.ptr.p_complex[1], 1, "Conj", ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(ae_c_abs(v, _state), _state), _state);
                ae_v_csub(&y3.ptr.p_complex[1], 1, &y1.ptr.p_complex[1], 1, "N", ae_v_len(1,i2-i1+1));
                v = ae_v_cdotproduct(&y3.ptr.p_complex[1], 1, "N", &y3.ptr.p_complex[1], 1, "Conj", ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(ae_c_abs(v, _state), _state), _state);
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ae_fp_greater(mverr,threshold);
    if( !silent )
    {
        printf("TESTING HERMITIAN BLAS\n");
        printf("MV error:                                %5.3e\n",
            (double)(mverr));
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testreflections(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t m;
    ae_int_t maxmn;
    ae_vector x;
    ae_vector v;
    ae_vector work;
    ae_matrix h;
    ae_matrix a;
    ae_matrix b;
    ae_matrix c;
    double tmp;
    double beta;
    double tau;
    double err;
    double mer;
    double mel;
    double meg;
    ae_int_t pass;
    ae_int_t passcount;
    double threshold;
    ae_int_t tasktype;
    double xscale;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);

    passcount = 10;
    threshold = 100*ae_machineepsilon;
    mer = 0;
    mel = 0;
    meg = 0;
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=10; n++)
        {
            for(m=1; m<=10; m++)
            {
                
                /*
                 * Task
                 */
                n = 1+ae_randominteger(10, _state);
                m = 1+ae_randominteger(10, _state);
                maxmn = ae_maxint(m, n, _state);
                
                /*
                 * Initialize
                 */
                ae_vector_set_length(&x, maxmn+1, _state);
                ae_vector_set_length(&v, maxmn+1, _state);
                ae_vector_set_length(&work, maxmn+1, _state);
                ae_matrix_set_length(&h, maxmn+1, maxmn+1, _state);
                ae_matrix_set_length(&a, maxmn+1, maxmn+1, _state);
                ae_matrix_set_length(&b, maxmn+1, maxmn+1, _state);
                ae_matrix_set_length(&c, maxmn+1, maxmn+1, _state);
                
                /*
                 * GenerateReflection, three tasks are possible:
                 * * random X
                 * * zero X
                 * * non-zero X[1], all other are zeros
                 * * random X, near underflow scale
                 * * random X, near overflow scale
                 */
                for(tasktype=0; tasktype<=4; tasktype++)
                {
                    xscale = 1;
                    if( tasktype==0 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    if( tasktype==1 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = 0;
                        }
                    }
                    if( tasktype==2 )
                    {
                        x.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                        for(i=2; i<=n; i++)
                        {
                            x.ptr.p_double[i] = 0;
                        }
                    }
                    if( tasktype==3 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = (ae_randominteger(21, _state)-10)*ae_minrealnumber;
                        }
                        xscale = 10*ae_minrealnumber;
                    }
                    if( tasktype==4 )
                    {
                        for(i=1; i<=n; i++)
                        {
                            x.ptr.p_double[i] = (2*ae_randomreal(_state)-1)*ae_maxrealnumber;
                        }
                        xscale = ae_maxrealnumber;
                    }
                    ae_v_move(&v.ptr.p_double[1], 1, &x.ptr.p_double[1], 1, ae_v_len(1,n));
                    generatereflection(&v, n, &tau, _state);
                    beta = v.ptr.p_double[1];
                    v.ptr.p_double[1] = 1;
                    for(i=1; i<=n; i++)
                    {
                        for(j=1; j<=n; j++)
                        {
                            if( i==j )
                            {
                                h.ptr.pp_double[i][j] = 1-tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                            }
                            else
                            {
                                h.ptr.pp_double[i][j] = -tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                            }
                        }
                    }
                    err = 0;
                    for(i=1; i<=n; i++)
                    {
                        tmp = ae_v_dotproduct(&h.ptr.pp_double[i][1], 1, &x.ptr.p_double[1], 1, ae_v_len(1,n));
                        if( i==1 )
                        {
                            err = ae_maxreal(err, ae_fabs(tmp-beta, _state), _state);
                        }
                        else
                        {
                            err = ae_maxreal(err, ae_fabs(tmp, _state), _state);
                        }
                    }
                    meg = ae_maxreal(meg, err/xscale, _state);
                }
                
                /*
                 * ApplyReflectionFromTheLeft
                 */
                for(i=1; i<=m; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    v.ptr.p_double[i] = x.ptr.p_double[i];
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        b.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                    }
                }
                generatereflection(&v, m, &tau, _state);
                beta = v.ptr.p_double[1];
                v.ptr.p_double[1] = 1;
                applyreflectionfromtheleft(&b, tau, &v, 1, m, 1, n, &work, _state);
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=m; j++)
                    {
                        if( i==j )
                        {
                            h.ptr.pp_double[i][j] = 1-tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                        else
                        {
                            h.ptr.pp_double[i][j] = -tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                    }
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        tmp = ae_v_dotproduct(&h.ptr.pp_double[i][1], 1, &a.ptr.pp_double[1][j], a.stride, ae_v_len(1,m));
                        c.ptr.pp_double[i][j] = tmp;
                    }
                }
                err = 0;
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(b.ptr.pp_double[i][j]-c.ptr.pp_double[i][j], _state), _state);
                    }
                }
                mel = ae_maxreal(mel, err, _state);
                
                /*
                 * ApplyReflectionFromTheRight
                 */
                for(i=1; i<=n; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    v.ptr.p_double[i] = x.ptr.p_double[i];
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        b.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                    }
                }
                generatereflection(&v, n, &tau, _state);
                beta = v.ptr.p_double[1];
                v.ptr.p_double[1] = 1;
                applyreflectionfromtheright(&b, tau, &v, 1, m, 1, n, &work, _state);
                for(i=1; i<=n; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        if( i==j )
                        {
                            h.ptr.pp_double[i][j] = 1-tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                        else
                        {
                            h.ptr.pp_double[i][j] = -tau*v.ptr.p_double[i]*v.ptr.p_double[j];
                        }
                    }
                }
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        tmp = ae_v_dotproduct(&a.ptr.pp_double[i][1], 1, &h.ptr.pp_double[1][j], h.stride, ae_v_len(1,n));
                        c.ptr.pp_double[i][j] = tmp;
                    }
                }
                err = 0;
                for(i=1; i<=m; i++)
                {
                    for(j=1; j<=n; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(b.ptr.pp_double[i][j]-c.ptr.pp_double[i][j], _state), _state);
                    }
                }
                mer = ae_maxreal(mer, err, _state);
            }
        }
    }
    
    /*
     * Overflow crash test
     */
    ae_vector_set_length(&x, 10+1, _state);
    ae_vector_set_length(&v, 10+1, _state);
    for(i=1; i<=10; i++)
    {
        v.ptr.p_double[i] = ae_maxrealnumber*0.01*(2*ae_randomreal(_state)-1);
    }
    generatereflection(&v, 10, &tau, _state);
    result = (ae_fp_less_eq(meg,threshold)&&ae_fp_less_eq(mel,threshold))&&ae_fp_less_eq(mer,threshold);
    if( !silent )
    {
        printf("TESTING REFLECTIONS\n");
        printf("Pass count is %0d\n",
            (int)(passcount));
        printf("Generate     absolute error is       %5.3e\n",
            (double)(meg));
        printf("Apply(Left)  absolute error is       %5.3e\n",
            (double)(mel));
        printf("Apply(Right) absolute error is       %5.3e\n",
            (double)(mer));
        printf("Overflow crash test passed\n");
        if( result )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    ae_frame_leave(_state);
    return result;
}








ae_bool testcreflections(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t m;
    ae_int_t maxmn;
    ae_vector x;
    ae_vector v;
    ae_vector work;
    ae_matrix h;
    ae_matrix a;
    ae_matrix b;
    ae_matrix c;
    ae_complex tmp;
    ae_complex beta;
    ae_complex tau;
    double err;
    double mer;
    double mel;
    double meg;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&v, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&work, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&h, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_COMPLEX, _state, ae_true);

    threshold = 1000*ae_machineepsilon;
    passcount = 1000;
    mer = 0;
    mel = 0;
    meg = 0;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Task
         */
        n = 1+ae_randominteger(10, _state);
        m = 1+ae_randominteger(10, _state);
        maxmn = ae_maxint(m, n, _state);
        
        /*
         * Initialize
         */
        ae_vector_set_length(&x, maxmn+1, _state);
        ae_vector_set_length(&v, maxmn+1, _state);
        ae_vector_set_length(&work, maxmn+1, _state);
        ae_matrix_set_length(&h, maxmn+1, maxmn+1, _state);
        ae_matrix_set_length(&a, maxmn+1, maxmn+1, _state);
        ae_matrix_set_length(&b, maxmn+1, maxmn+1, _state);
        ae_matrix_set_length(&c, maxmn+1, maxmn+1, _state);
        
        /*
         * GenerateReflection
         */
        for(i=1; i<=n; i++)
        {
            x.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            x.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            v.ptr.p_complex[i] = x.ptr.p_complex[i];
        }
        complexgeneratereflection(&v, n, &tau, _state);
        beta = v.ptr.p_complex[1];
        v.ptr.p_complex[1] = ae_complex_from_d(1);
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                if( i==j )
                {
                    h.ptr.pp_complex[i][j] = ae_c_d_sub(1,ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
                else
                {
                    h.ptr.pp_complex[i][j] = ae_c_neg(ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
            }
        }
        err = 0;
        for(i=1; i<=n; i++)
        {
            tmp = ae_v_cdotproduct(&h.ptr.pp_complex[1][i], h.stride, "Conj", &x.ptr.p_complex[1], 1, "N", ae_v_len(1,n));
            if( i==1 )
            {
                err = ae_maxreal(err, ae_c_abs(ae_c_sub(tmp,beta), _state), _state);
            }
            else
            {
                err = ae_maxreal(err, ae_c_abs(tmp, _state), _state);
            }
        }
        err = ae_maxreal(err, ae_fabs(beta.y, _state), _state);
        meg = ae_maxreal(meg, err, _state);
        
        /*
         * ApplyReflectionFromTheLeft
         */
        for(i=1; i<=m; i++)
        {
            x.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            x.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            v.ptr.p_complex[i] = x.ptr.p_complex[i];
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                b.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        complexgeneratereflection(&v, m, &tau, _state);
        beta = v.ptr.p_complex[1];
        v.ptr.p_complex[1] = ae_complex_from_d(1);
        complexapplyreflectionfromtheleft(&b, tau, &v, 1, m, 1, n, &work, _state);
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=m; j++)
            {
                if( i==j )
                {
                    h.ptr.pp_complex[i][j] = ae_c_d_sub(1,ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
                else
                {
                    h.ptr.pp_complex[i][j] = ae_c_neg(ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
            }
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                tmp = ae_v_cdotproduct(&h.ptr.pp_complex[i][1], 1, "N", &a.ptr.pp_complex[1][j], a.stride, "N", ae_v_len(1,m));
                c.ptr.pp_complex[i][j] = tmp;
            }
        }
        err = 0;
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                err = ae_maxreal(err, ae_c_abs(ae_c_sub(b.ptr.pp_complex[i][j],c.ptr.pp_complex[i][j]), _state), _state);
            }
        }
        mel = ae_maxreal(mel, err, _state);
        
        /*
         * ApplyReflectionFromTheRight
         */
        for(i=1; i<=n; i++)
        {
            x.ptr.p_complex[i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            v.ptr.p_complex[i] = x.ptr.p_complex[i];
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                a.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                b.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
            }
        }
        complexgeneratereflection(&v, n, &tau, _state);
        beta = v.ptr.p_complex[1];
        v.ptr.p_complex[1] = ae_complex_from_d(1);
        complexapplyreflectionfromtheright(&b, tau, &v, 1, m, 1, n, &work, _state);
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                if( i==j )
                {
                    h.ptr.pp_complex[i][j] = ae_c_d_sub(1,ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
                else
                {
                    h.ptr.pp_complex[i][j] = ae_c_neg(ae_c_mul(ae_c_mul(tau,v.ptr.p_complex[i]),ae_c_conj(v.ptr.p_complex[j], _state)));
                }
            }
        }
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                tmp = ae_v_cdotproduct(&a.ptr.pp_complex[i][1], 1, "N", &h.ptr.pp_complex[1][j], h.stride, "N", ae_v_len(1,n));
                c.ptr.pp_complex[i][j] = tmp;
            }
        }
        err = 0;
        for(i=1; i<=m; i++)
        {
            for(j=1; j<=n; j++)
            {
                err = ae_maxreal(err, ae_c_abs(ae_c_sub(b.ptr.pp_complex[i][j],c.ptr.pp_complex[i][j]), _state), _state);
            }
        }
        mer = ae_maxreal(mer, err, _state);
    }
    
    /*
     * Overflow crash test
     */
    ae_vector_set_length(&x, 10+1, _state);
    ae_vector_set_length(&v, 10+1, _state);
    for(i=1; i<=10; i++)
    {
        v.ptr.p_complex[i] = ae_complex_from_d(ae_maxrealnumber*0.01*(2*ae_randomreal(_state)-1));
    }
    complexgeneratereflection(&v, 10, &tau, _state);
    
    /*
     * report
     */
    waserrors = (ae_fp_greater(meg,threshold)||ae_fp_greater(mel,threshold))||ae_fp_greater(mer,threshold);
    if( !silent )
    {
        printf("TESTING COMPLEX REFLECTIONS\n");
        printf("Generate error:                          %5.3e\n",
            (double)(meg));
        printf("Apply(L) error:                          %5.3e\n",
            (double)(mel));
        printf("Apply(R) error:                          %5.3e\n",
            (double)(mer));
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        printf("Overflow crash test:                     PASSED\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testsblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ua;
    ae_matrix la;
    ae_vector x;
    ae_vector y1;
    ae_vector y2;
    ae_vector y3;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t i1;
    ae_int_t i2;
    ae_bool waserrors;
    double mverr;
    double threshold;
    double alpha;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y3, 0, DT_REAL, _state, ae_true);

    mverr = 0;
    waserrors = ae_false;
    maxn = 10;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Test MV
     */
    for(n=2; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n+1, n+1, _state);
        ae_matrix_set_length(&ua, n+1, n+1, _state);
        ae_matrix_set_length(&la, n+1, n+1, _state);
        ae_vector_set_length(&x, n+1, _state);
        ae_vector_set_length(&y1, n+1, _state);
        ae_vector_set_length(&y2, n+1, _state);
        ae_vector_set_length(&y3, n+1, _state);
        
        /*
         * fill A, UA, LA
         */
        for(i=1; i<=n; i++)
        {
            a.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
            for(j=i+1; j<=n; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                ua.ptr.pp_double[i][j] = 0;
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=i; j<=n; j++)
            {
                ua.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=n; j++)
            {
                la.ptr.pp_double[i][j] = 0;
            }
        }
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=i; j++)
            {
                la.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
            }
        }
        
        /*
         * test on different I1, I2
         */
        for(i1=1; i1<=n; i1++)
        {
            for(i2=i1; i2<=n; i2++)
            {
                
                /*
                 * Fill X, choose Alpha
                 */
                for(i=1; i<=i2-i1+1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                alpha = 2*ae_randomreal(_state)-1;
                
                /*
                 * calculate A*x, UA*x, LA*x
                 */
                for(i=i1; i<=i2; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][i1], 1, &x.ptr.p_double[1], 1, ae_v_len(i1,i2));
                    y1.ptr.p_double[i-i1+1] = alpha*v;
                }
                symmetricmatrixvectormultiply(&ua, ae_true, i1, i2, &x, alpha, &y2, _state);
                symmetricmatrixvectormultiply(&la, ae_false, i1, i2, &x, alpha, &y3, _state);
                
                /*
                 * Calculate error
                 */
                ae_v_sub(&y2.ptr.p_double[1], 1, &y1.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                v = ae_v_dotproduct(&y2.ptr.p_double[1], 1, &y2.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(v, _state), _state);
                ae_v_sub(&y3.ptr.p_double[1], 1, &y1.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                v = ae_v_dotproduct(&y3.ptr.p_double[1], 1, &y3.ptr.p_double[1], 1, ae_v_len(1,i2-i1+1));
                mverr = ae_maxreal(mverr, ae_sqrt(v, _state), _state);
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ae_fp_greater(mverr,threshold);
    if( !silent )
    {
        printf("TESTING SYMMETRIC BLAS\n");
        printf("MV error:                                %5.3e\n",
            (double)(mverr));
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static double testortfacunit_rmatrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state);
static void testortfacunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testortfacunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static void testortfacunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testortfacunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testortfacunit_internalmatrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     ae_state *_state);
static void testortfacunit_testrqrproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state);
static void testortfacunit_testcqrproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state);
static void testortfacunit_testrlqproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state);
static void testortfacunit_testclqproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state);
static void testortfacunit_testrbdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* bderrors,
     ae_state *_state);
static void testortfacunit_testrhessproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* hesserrors,
     ae_state *_state);
static void testortfacunit_testrtdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state);
static void testortfacunit_testctdproblem(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state);





/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testortfac(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxmn;
    double threshold;
    ae_int_t passcount;
    ae_int_t mx;
    ae_matrix ra;
    ae_matrix ca;
    ae_int_t m;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_bool rqrerrors;
    ae_bool rlqerrors;
    ae_bool cqrerrors;
    ae_bool clqerrors;
    ae_bool rbderrors;
    ae_bool rhesserrors;
    ae_bool rtderrors;
    ae_bool ctderrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);

    waserrors = ae_false;
    rqrerrors = ae_false;
    rlqerrors = ae_false;
    cqrerrors = ae_false;
    clqerrors = ae_false;
    rbderrors = ae_false;
    rhesserrors = ae_false;
    rtderrors = ae_false;
    ctderrors = ae_false;
    maxmn = 3*ablasblocksize(&ra, _state)+1;
    passcount = 1;
    threshold = 5*1000*ae_machineepsilon;
    
    /*
     * Different problems
     */
    for(mx=1; mx<=maxmn; mx++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Rectangular factorizations: QR, LQ, bidiagonal
             * Matrix types: zero, dense, sparse
             */
            n = 1+ae_randominteger(mx, _state);
            m = 1+ae_randominteger(mx, _state);
            if( ae_fp_greater(ae_randomreal(_state),0.5) )
            {
                n = mx;
            }
            else
            {
                m = mx;
            }
            ae_matrix_set_length(&ra, m, n, _state);
            ae_matrix_set_length(&ca, m, n, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ra.ptr.pp_double[i][j] = 0;
                    ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                }
            }
            testortfacunit_testrqrproblem(&ra, m, n, threshold, &rqrerrors, _state);
            testortfacunit_testrlqproblem(&ra, m, n, threshold, &rlqerrors, _state);
            testortfacunit_testcqrproblem(&ca, m, n, threshold, &cqrerrors, _state);
            testortfacunit_testclqproblem(&ca, m, n, threshold, &clqerrors, _state);
            testortfacunit_testrbdproblem(&ra, m, n, threshold, &rbderrors, _state);
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                    ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                }
            }
            testortfacunit_testrqrproblem(&ra, m, n, threshold, &rqrerrors, _state);
            testortfacunit_testrlqproblem(&ra, m, n, threshold, &rlqerrors, _state);
            testortfacunit_testcqrproblem(&ca, m, n, threshold, &cqrerrors, _state);
            testortfacunit_testclqproblem(&ca, m, n, threshold, &clqerrors, _state);
            testortfacunit_testrbdproblem(&ra, m, n, threshold, &rbderrors, _state);
            testortfacunit_rmatrixfillsparsea(&ra, m, n, 0.95, _state);
            testortfacunit_cmatrixfillsparsea(&ca, m, n, 0.95, _state);
            testortfacunit_testrqrproblem(&ra, m, n, threshold, &rqrerrors, _state);
            testortfacunit_testrlqproblem(&ra, m, n, threshold, &rlqerrors, _state);
            testortfacunit_testcqrproblem(&ca, m, n, threshold, &cqrerrors, _state);
            testortfacunit_testclqproblem(&ca, m, n, threshold, &clqerrors, _state);
            testortfacunit_testrbdproblem(&ra, m, n, threshold, &rbderrors, _state);
            
            /*
             * Square factorizations: Hessenberg, tridiagonal
             * Matrix types: zero, dense, sparse
             */
            ae_matrix_set_length(&ra, mx, mx, _state);
            ae_matrix_set_length(&ca, mx, mx, _state);
            for(i=0; i<=mx-1; i++)
            {
                for(j=0; j<=mx-1; j++)
                {
                    ra.ptr.pp_double[i][j] = 0;
                    ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                }
            }
            testortfacunit_testrhessproblem(&ra, mx, threshold, &rhesserrors, _state);
            for(i=0; i<=mx-1; i++)
            {
                for(j=0; j<=mx-1; j++)
                {
                    ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                    ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                }
            }
            testortfacunit_testrhessproblem(&ra, mx, threshold, &rhesserrors, _state);
            testortfacunit_rmatrixfillsparsea(&ra, mx, mx, 0.95, _state);
            testortfacunit_cmatrixfillsparsea(&ca, mx, mx, 0.95, _state);
            testortfacunit_testrhessproblem(&ra, mx, threshold, &rhesserrors, _state);
            
            /*
             * Symetric factorizations: tridiagonal
             * Matrix types: zero, dense, sparse
             */
            ae_matrix_set_length(&ra, mx, mx, _state);
            ae_matrix_set_length(&ca, mx, mx, _state);
            for(i=0; i<=mx-1; i++)
            {
                for(j=0; j<=mx-1; j++)
                {
                    ra.ptr.pp_double[i][j] = 0;
                    ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                }
            }
            testortfacunit_testrtdproblem(&ra, mx, threshold, &rtderrors, _state);
            testortfacunit_testctdproblem(&ca, mx, threshold, &ctderrors, _state);
            for(i=0; i<=mx-1; i++)
            {
                for(j=i; j<=mx-1; j++)
                {
                    ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                    ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                    ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
                    ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
                }
            }
            for(i=0; i<=mx-1; i++)
            {
                ca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            }
            testortfacunit_testrtdproblem(&ra, mx, threshold, &rtderrors, _state);
            testortfacunit_testctdproblem(&ca, mx, threshold, &ctderrors, _state);
            testortfacunit_rmatrixfillsparsea(&ra, mx, mx, 0.95, _state);
            testortfacunit_cmatrixfillsparsea(&ca, mx, mx, 0.95, _state);
            for(i=0; i<=mx-1; i++)
            {
                for(j=i; j<=mx-1; j++)
                {
                    ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
                    ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
                }
            }
            for(i=0; i<=mx-1; i++)
            {
                ca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            }
            testortfacunit_testrtdproblem(&ra, mx, threshold, &rtderrors, _state);
            testortfacunit_testctdproblem(&ca, mx, threshold, &ctderrors, _state);
        }
    }
    
    /*
     * report
     */
    waserrors = ((((((rqrerrors||rlqerrors)||cqrerrors)||clqerrors)||rbderrors)||rhesserrors)||rtderrors)||ctderrors;
    if( !silent )
    {
        printf("TESTING ORTFAC UNIT\n");
        printf("RQR ERRORS:                              ");
        if( !rqrerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RLQ ERRORS:                              ");
        if( !rlqerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("CQR ERRORS:                              ");
        if( !cqrerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("CLQ ERRORS:                              ");
        if( !clqerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RBD ERRORS:                              ");
        if( !rbderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RHESS ERRORS:                            ");
        if( !rhesserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("RTD ERRORS:                              ");
        if( !rtderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("CTD ERRORS:                              ");
        if( !ctderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Diff
*************************************************************************/
static double testortfacunit_rmatrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double result;


    result = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            result = ae_maxreal(result, ae_fabs(b->ptr.pp_double[i][j]-a->ptr.pp_double[i][j], _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testortfacunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testortfacunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Sparse fill
*************************************************************************/
static void testortfacunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_double[i][j] = 0;
            }
        }
    }
}


/*************************************************************************
Sparse fill
*************************************************************************/
static void testortfacunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a->ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_d(0);
            }
        }
    }
}


/*************************************************************************
Matrix multiplication
*************************************************************************/
static void testortfacunit_internalmatrixmatrixmultiply(/* Real    */ ae_matrix* a,
     ae_int_t ai1,
     ae_int_t ai2,
     ae_int_t aj1,
     ae_int_t aj2,
     ae_bool transa,
     /* Real    */ ae_matrix* b,
     ae_int_t bi1,
     ae_int_t bi2,
     ae_int_t bj1,
     ae_int_t bj2,
     ae_bool transb,
     /* Real    */ ae_matrix* c,
     ae_int_t ci1,
     ae_int_t ci2,
     ae_int_t cj1,
     ae_int_t cj2,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t arows;
    ae_int_t acols;
    ae_int_t brows;
    ae_int_t bcols;
    ae_int_t crows;
    ae_int_t ccols;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t l;
    ae_int_t r;
    double v;
    ae_vector work;
    double beta;
    double alpha;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    
    /*
     * Pre-setup
     */
    k = ae_maxint(ai2-ai1+1, aj2-aj1+1, _state);
    k = ae_maxint(k, bi2-bi1+1, _state);
    k = ae_maxint(k, bj2-bj1+1, _state);
    ae_vector_set_length(&work, k+1, _state);
    beta = 0;
    alpha = 1;
    
    /*
     * Setup
     */
    if( !transa )
    {
        arows = ai2-ai1+1;
        acols = aj2-aj1+1;
    }
    else
    {
        arows = aj2-aj1+1;
        acols = ai2-ai1+1;
    }
    if( !transb )
    {
        brows = bi2-bi1+1;
        bcols = bj2-bj1+1;
    }
    else
    {
        brows = bj2-bj1+1;
        bcols = bi2-bi1+1;
    }
    ae_assert(acols==brows, "MatrixMatrixMultiply: incorrect matrix sizes!", _state);
    if( ((arows<=0||acols<=0)||brows<=0)||bcols<=0 )
    {
        ae_frame_leave(_state);
        return;
    }
    crows = arows;
    ccols = bcols;
    
    /*
     * Test WORK
     */
    i = ae_maxint(arows, acols, _state);
    i = ae_maxint(brows, i, _state);
    i = ae_maxint(i, bcols, _state);
    work.ptr.p_double[1] = 0;
    work.ptr.p_double[i] = 0;
    
    /*
     * Prepare C
     */
    if( ae_fp_eq(beta,0) )
    {
        for(i=ci1; i<=ci2; i++)
        {
            for(j=cj1; j<=cj2; j++)
            {
                c->ptr.pp_double[i][j] = 0;
            }
        }
    }
    else
    {
        for(i=ci1; i<=ci2; i++)
        {
            ae_v_muld(&c->ptr.pp_double[i][cj1], 1, ae_v_len(cj1,cj2), beta);
        }
    }
    
    /*
     * A*B
     */
    if( !transa&&!transb )
    {
        for(l=ai1; l<=ai2; l++)
        {
            for(r=bi1; r<=bi2; r++)
            {
                v = alpha*a->ptr.pp_double[l][aj1+r-bi1];
                k = ci1+l-ai1;
                ae_v_addd(&c->ptr.pp_double[k][cj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(cj1,cj2), v);
            }
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * A*B'
     */
    if( !transa&&transb )
    {
        if( arows*acols<brows*bcols )
        {
            for(r=bi1; r<=bi2; r++)
            {
                for(l=ai1; l<=ai2; l++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[l][aj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(aj1,aj2));
                    c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1] = c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1]+alpha*v;
                }
            }
            ae_frame_leave(_state);
            return;
        }
        else
        {
            for(l=ai1; l<=ai2; l++)
            {
                for(r=bi1; r<=bi2; r++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[l][aj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(aj1,aj2));
                    c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1] = c->ptr.pp_double[ci1+l-ai1][cj1+r-bi1]+alpha*v;
                }
            }
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * A'*B
     */
    if( transa&&!transb )
    {
        for(l=aj1; l<=aj2; l++)
        {
            for(r=bi1; r<=bi2; r++)
            {
                v = alpha*a->ptr.pp_double[ai1+r-bi1][l];
                k = ci1+l-aj1;
                ae_v_addd(&c->ptr.pp_double[k][cj1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(cj1,cj2), v);
            }
        }
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * A'*B'
     */
    if( transa&&transb )
    {
        if( arows*acols<brows*bcols )
        {
            for(r=bi1; r<=bi2; r++)
            {
                for(i=1; i<=crows; i++)
                {
                    work.ptr.p_double[i] = 0.0;
                }
                for(l=ai1; l<=ai2; l++)
                {
                    v = alpha*b->ptr.pp_double[r][bj1+l-ai1];
                    k = cj1+r-bi1;
                    ae_v_addd(&work.ptr.p_double[1], 1, &a->ptr.pp_double[l][aj1], 1, ae_v_len(1,crows), v);
                }
                ae_v_add(&c->ptr.pp_double[ci1][k], c->stride, &work.ptr.p_double[1], 1, ae_v_len(ci1,ci2));
            }
            ae_frame_leave(_state);
            return;
        }
        else
        {
            for(l=aj1; l<=aj2; l++)
            {
                k = ai2-ai1+1;
                ae_v_move(&work.ptr.p_double[1], 1, &a->ptr.pp_double[ai1][l], a->stride, ae_v_len(1,k));
                for(r=bi1; r<=bi2; r++)
                {
                    v = ae_v_dotproduct(&work.ptr.p_double[1], 1, &b->ptr.pp_double[r][bj1], 1, ae_v_len(1,k));
                    c->ptr.pp_double[ci1+l-aj1][cj1+r-bi1] = c->ptr.pp_double[ci1+l-aj1][cj1+r-bi1]+alpha*v;
                }
            }
            ae_frame_leave(_state);
            return;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrqrproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix r;
    ae_matrix q2;
    double v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&taub, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_rmatrixmakeacopy(a, m, n, &b, _state);
    rmatrixqr(&b, m, n, &taub, _state);
    rmatrixqrunpackq(&b, m, n, &taub, m, &q, _state);
    rmatrixqrunpackr(&b, m, n, &r, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &r.ptr.pp_double[0][j], r.stride, ae_v_len(0,m-1));
            *qrerrors = *qrerrors||ae_fp_greater(ae_fabs(v-a->ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=ae_minint(i, n-1, _state)-1; j++)
        {
            *qrerrors = *qrerrors||ae_fp_neq(r.ptr.pp_double[i][j],0);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,m-1));
            if( i==j )
            {
                v = v-1;
            }
            *qrerrors = *qrerrors||ae_fp_greater_eq(ae_fabs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(m, _state);
    rmatrixqrunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            *qrerrors = *qrerrors||ae_fp_greater(ae_fabs(q2.ptr.pp_double[i][j]-q.ptr.pp_double[i][j], _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testcqrproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* qrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix r;
    ae_matrix q2;
    ae_complex v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&taub, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_cmatrixmakeacopy(a, m, n, &b, _state);
    cmatrixqr(&b, m, n, &taub, _state);
    cmatrixqrunpackq(&b, m, n, &taub, m, &q, _state);
    cmatrixqrunpackr(&b, m, n, &r, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &r.ptr.pp_complex[0][j], r.stride, "N", ae_v_len(0,m-1));
            *qrerrors = *qrerrors||ae_fp_greater(ae_c_abs(ae_c_sub(v,a->ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=ae_minint(i, n-1, _state)-1; j++)
        {
            *qrerrors = *qrerrors||ae_c_neq_d(r.ptr.pp_complex[i][j],0);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,m-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *qrerrors = *qrerrors||ae_fp_greater_eq(ae_c_abs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(m, _state);
    cmatrixqrunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            *qrerrors = *qrerrors||ae_fp_greater(ae_c_abs(ae_c_sub(q2.ptr.pp_complex[i][j],q.ptr.pp_complex[i][j]), _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrlqproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix l;
    ae_matrix q2;
    double v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&taub, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&l, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_rmatrixmakeacopy(a, m, n, &b, _state);
    rmatrixlq(&b, m, n, &taub, _state);
    rmatrixlqunpackq(&b, m, n, &taub, n, &q, _state);
    rmatrixlqunpackl(&b, m, n, &l, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&l.ptr.pp_double[i][0], 1, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_fabs(v-a->ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=ae_minint(i, n-1, _state)+1; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_fp_neq(l.ptr.pp_double[i][j],0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_fabs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(n, _state);
    rmatrixlqunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_fp_greater(ae_fabs(q2.ptr.pp_double[i][j]-q.ptr.pp_double[i][j], _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testclqproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* lqerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix b;
    ae_vector taub;
    ae_matrix q;
    ae_matrix l;
    ae_matrix q2;
    ae_complex v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&taub, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&l, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * Test decompose-and-unpack error
     */
    testortfacunit_cmatrixmakeacopy(a, m, n, &b, _state);
    cmatrixlq(&b, m, n, &taub, _state);
    cmatrixlqunpackq(&b, m, n, &taub, n, &q, _state);
    cmatrixlqunpackl(&b, m, n, &l, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&l.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[0][j], q.stride, "N", ae_v_len(0,n-1));
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_c_abs(ae_c_sub(v,a->ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=ae_minint(i, n-1, _state)+1; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_c_neq_d(l.ptr.pp_complex[i][j],0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *lqerrors = *lqerrors||ae_fp_greater_eq(ae_c_abs(v, _state),threshold);
        }
    }
    
    /*
     * Test for other errors
     */
    k = 1+ae_randominteger(n, _state);
    cmatrixlqunpackq(&b, m, n, &taub, k, &q2, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *lqerrors = *lqerrors||ae_fp_greater(ae_c_abs(ae_c_sub(q2.ptr.pp_complex[i][j],q.ptr.pp_complex[i][j]), _state),10*ae_machineepsilon);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrbdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* bderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_matrix t;
    ae_matrix pt;
    ae_matrix q;
    ae_matrix r;
    ae_matrix bd;
    ae_matrix x;
    ae_matrix r1;
    ae_matrix r2;
    ae_vector taup;
    ae_vector tauq;
    ae_vector d;
    ae_vector e;
    ae_bool up;
    double v;
    ae_int_t mtsize;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&t, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&pt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bd, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&taup, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tauq, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    
    /*
     * Bidiagonal decomposition error
     */
    testortfacunit_rmatrixmakeacopy(a, m, n, &t, _state);
    rmatrixbd(&t, m, n, &tauq, &taup, _state);
    rmatrixbdunpackq(&t, m, n, &tauq, m, &q, _state);
    rmatrixbdunpackpt(&t, m, n, &taup, n, &pt, _state);
    rmatrixbdunpackdiagonals(&t, m, n, &up, &d, &e, _state);
    ae_matrix_set_length(&bd, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            bd.ptr.pp_double[i][j] = 0;
        }
    }
    for(i=0; i<=ae_minint(m, n, _state)-1; i++)
    {
        bd.ptr.pp_double[i][i] = d.ptr.p_double[i];
    }
    if( up )
    {
        for(i=0; i<=ae_minint(m, n, _state)-2; i++)
        {
            bd.ptr.pp_double[i][i+1] = e.ptr.p_double[i];
        }
    }
    else
    {
        for(i=0; i<=ae_minint(m, n, _state)-2; i++)
        {
            bd.ptr.pp_double[i+1][i] = e.ptr.p_double[i];
        }
    }
    ae_matrix_set_length(&r, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &bd.ptr.pp_double[0][j], bd.stride, ae_v_len(0,m-1));
            r.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&r.ptr.pp_double[i][0], 1, &pt.ptr.pp_double[0][j], pt.stride, ae_v_len(0,n-1));
            *bderrors = *bderrors||ae_fp_greater(ae_fabs(v-a->ptr.pp_double[i][j], _state),threshold);
        }
    }
    
    /*
     * Orthogonality test for Q/PT
     */
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,m-1));
            if( i==j )
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
            }
            else
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&pt.ptr.pp_double[i][0], 1, &pt.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
            }
            else
            {
                *bderrors = *bderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
            }
        }
    }
    
    /*
     * Partial unpacking test
     */
    k = 1+ae_randominteger(m, _state);
    rmatrixbdunpackq(&t, m, n, &tauq, k, &r, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=k-1; j++)
        {
            *bderrors = *bderrors||ae_fp_greater(ae_fabs(r.ptr.pp_double[i][j]-q.ptr.pp_double[i][j], _state),10*ae_machineepsilon);
        }
    }
    k = 1+ae_randominteger(n, _state);
    rmatrixbdunpackpt(&t, m, n, &taup, k, &r, _state);
    for(i=0; i<=k-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *bderrors = *bderrors||ae_fp_neq(r.ptr.pp_double[i][j]-pt.ptr.pp_double[i][j],0);
        }
    }
    
    /*
     * Multiplication test
     */
    ae_matrix_set_length(&x, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    ae_matrix_set_length(&r, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    ae_matrix_set_length(&r1, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    ae_matrix_set_length(&r2, ae_maxint(m, n, _state)-1+1, ae_maxint(m, n, _state)-1+1, _state);
    for(i=0; i<=ae_maxint(m, n, _state)-1; i++)
    {
        for(j=0; j<=ae_maxint(m, n, _state)-1; j++)
        {
            x.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
        }
    }
    mtsize = 1+ae_randominteger(ae_maxint(m, n, _state), _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, m-1, ae_false, &q, 0, m-1, 0, m-1, ae_false, &r1, 0, mtsize-1, 0, m-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, mtsize, m, ae_true, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, m, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, m-1, ae_false, &q, 0, m-1, 0, m-1, ae_true, &r1, 0, mtsize-1, 0, m-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, m, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, mtsize, m, ae_true, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, m, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&q, 0, m-1, 0, m-1, ae_false, &r, 0, m-1, 0, mtsize-1, ae_false, &r1, 0, m-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, m, mtsize, ae_false, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, m, mtsize, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&q, 0, m-1, 0, m-1, ae_true, &r, 0, m-1, 0, mtsize-1, ae_false, &r1, 0, m-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, m, mtsize, &r2, _state);
    rmatrixbdmultiplybyq(&t, m, n, &tauq, &r2, m, mtsize, ae_false, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, m, mtsize, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, n-1, ae_false, &pt, 0, n-1, 0, n-1, ae_true, &r1, 0, mtsize-1, 0, n-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, mtsize, n, ae_true, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, n, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&r, 0, mtsize-1, 0, n-1, ae_false, &pt, 0, n-1, 0, n-1, ae_false, &r1, 0, mtsize-1, 0, n-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, mtsize, n, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, mtsize, n, ae_true, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, mtsize, n, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&pt, 0, n-1, 0, n-1, ae_true, &r, 0, n-1, 0, mtsize-1, ae_false, &r1, 0, n-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, n, mtsize, ae_false, ae_false, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, n, mtsize, _state),threshold);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r, _state);
    testortfacunit_internalmatrixmatrixmultiply(&pt, 0, n-1, 0, n-1, ae_false, &r, 0, n-1, 0, mtsize-1, ae_false, &r1, 0, n-1, 0, mtsize-1, _state);
    testortfacunit_rmatrixmakeacopy(&x, n, mtsize, &r2, _state);
    rmatrixbdmultiplybyp(&t, m, n, &taup, &r2, n, mtsize, ae_false, ae_true, _state);
    *bderrors = *bderrors||ae_fp_greater(testortfacunit_rmatrixdiff(&r1, &r2, n, mtsize, _state),threshold);
    ae_frame_leave(_state);
}


/*************************************************************************
Problem testing
*************************************************************************/
static void testortfacunit_testrhessproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* hesserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix b;
    ae_matrix h;
    ae_matrix q;
    ae_matrix t1;
    ae_matrix t2;
    ae_vector tau;
    ae_int_t i;
    ae_int_t j;
    double v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tau, 0, DT_REAL, _state, ae_true);

    testortfacunit_rmatrixmakeacopy(a, n, n, &b, _state);
    
    /*
     * Decomposition
     */
    rmatrixhessenberg(&b, n, &tau, _state);
    rmatrixhessenbergunpackq(&b, n, &tau, &q, _state);
    rmatrixhessenbergunpackh(&b, n, &h, _state);
    
    /*
     * Matrix properties
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *hesserrors = *hesserrors||ae_fp_greater(ae_fabs(v, _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i-2; j++)
        {
            *hesserrors = *hesserrors||ae_fp_neq(h.ptr.pp_double[i][j],0);
        }
    }
    
    /*
     * Decomposition error
     */
    ae_matrix_set_length(&t1, n, n, _state);
    ae_matrix_set_length(&t2, n, n, _state);
    testortfacunit_internalmatrixmatrixmultiply(&q, 0, n-1, 0, n-1, ae_false, &h, 0, n-1, 0, n-1, ae_false, &t1, 0, n-1, 0, n-1, _state);
    testortfacunit_internalmatrixmatrixmultiply(&t1, 0, n-1, 0, n-1, ae_false, &q, 0, n-1, 0, n-1, ae_true, &t2, 0, n-1, 0, n-1, _state);
    *hesserrors = *hesserrors||ae_fp_greater(testortfacunit_rmatrixdiff(&t2, a, n, n, _state),threshold);
    ae_frame_leave(_state);
}


/*************************************************************************
Tridiagonal tester
*************************************************************************/
static void testortfacunit_testrtdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_matrix ua;
    ae_matrix la;
    ae_matrix t;
    ae_matrix q;
    ae_matrix t2;
    ae_matrix t3;
    ae_vector tau;
    ae_vector d;
    ae_vector e;
    double v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t3, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tau, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&ua, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&la, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&q, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t2, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t3, n-1+1, n-1+1, _state);
    
    /*
     * fill
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ua.ptr.pp_double[i][j] = 0;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=i; j<=n-1; j++)
        {
            ua.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            la.ptr.pp_double[i][j] = 0;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i; j++)
        {
            la.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    
    /*
     * Test 2tridiagonal: upper
     */
    smatrixtd(&ua, n, ae_true, &tau, &d, &e, _state);
    smatrixtdunpackq(&ua, n, ae_true, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_double[i][j] = 0;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_double[i][i] = d.ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_double[i][i+1] = e.ptr.p_double[i];
        t.ptr.pp_double[i+1][i] = e.ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            t2.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&t2.ptr.pp_double[i][0], 1, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            t3.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(t3.ptr.pp_double[i][j]-t.ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
        }
    }
    
    /*
     * Test 2tridiagonal: lower
     */
    smatrixtd(&la, n, ae_false, &tau, &d, &e, _state);
    smatrixtdunpackq(&la, n, ae_false, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_double[i][j] = 0;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_double[i][i] = d.ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_double[i][i+1] = e.ptr.p_double[i];
        t.ptr.pp_double[i+1][i] = e.ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[0][i], q.stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            t2.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&t2.ptr.pp_double[i][0], 1, &q.ptr.pp_double[0][j], q.stride, ae_v_len(0,n-1));
            t3.ptr.pp_double[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(t3.ptr.pp_double[i][j]-t.ptr.pp_double[i][j], _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&q.ptr.pp_double[i][0], 1, &q.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            *tderrors = *tderrors||ae_fp_greater(ae_fabs(v, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Hermitian problem tester
*************************************************************************/
static void testortfacunit_testctdproblem(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_matrix ua;
    ae_matrix la;
    ae_matrix t;
    ae_matrix q;
    ae_matrix t2;
    ae_matrix t3;
    ae_vector tau;
    ae_vector d;
    ae_vector e;
    ae_complex v;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&la, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&t, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&q, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&t2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&t3, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tau, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&ua, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&la, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&q, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t2, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&t3, n-1+1, n-1+1, _state);
    
    /*
     * fill
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ua.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=i; j<=n-1; j++)
        {
            ua.ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            la.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i; j++)
        {
            la.ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
    
    /*
     * Test 2tridiagonal: upper
     */
    hmatrixtd(&ua, n, ae_true, &tau, &d, &e, _state);
    hmatrixtdunpackq(&ua, n, ae_true, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_complex[i][i] = ae_complex_from_d(d.ptr.p_double[i]);
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_complex[i][i+1] = ae_complex_from_d(e.ptr.p_double[i]);
        t.ptr.pp_complex[i+1][i] = ae_complex_from_d(e.ptr.p_double[i]);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[0][i], q.stride, "Conj", &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,n-1));
            t2.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&t2.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[0][j], q.stride, "N", ae_v_len(0,n-1));
            t3.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(ae_c_sub(t3.ptr.pp_complex[i][j],t.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(v, _state),threshold);
        }
    }
    
    /*
     * Test 2tridiagonal: lower
     */
    hmatrixtd(&la, n, ae_false, &tau, &d, &e, _state);
    hmatrixtdunpackq(&la, n, ae_false, &tau, &q, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        t.ptr.pp_complex[i][i] = ae_complex_from_d(d.ptr.p_double[i]);
    }
    for(i=0; i<=n-2; i++)
    {
        t.ptr.pp_complex[i][i+1] = ae_complex_from_d(e.ptr.p_double[i]);
        t.ptr.pp_complex[i+1][i] = ae_complex_from_d(e.ptr.p_double[i]);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[0][i], q.stride, "Conj", &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,n-1));
            t2.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&t2.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[0][j], q.stride, "N", ae_v_len(0,n-1));
            t3.ptr.pp_complex[i][j] = v;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(ae_c_sub(t3.ptr.pp_complex[i][j],t.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&q.ptr.pp_complex[i][0], 1, "N", &q.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            *tderrors = *tderrors||ae_fp_greater(ae_c_abs(v, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}



static void testevdunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testevdunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testevdunit_rmatrixsymmetricsplit(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_state *_state);
static void testevdunit_cmatrixhermitiansplit(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_state *_state);
static void testevdunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state);
static void testevdunit_cunset2d(/* Complex */ ae_matrix* a,
     ae_state *_state);
static void testevdunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state);
static void testevdunit_cunset1d(/* Complex */ ae_vector* a,
     ae_state *_state);
static double testevdunit_tdtestproduct(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state);
static double testevdunit_testproduct(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state);
static double testevdunit_testort(/* Real    */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state);
static double testevdunit_testcproduct(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state);
static double testevdunit_testcort(/* Complex */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state);
static void testevdunit_testsevdproblem(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testhevdproblem(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testsevdbiproblem(/* Real    */ ae_matrix* afull,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testhevdbiproblem(/* Complex */ ae_matrix* afull,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testtdevdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testtdevdbiproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testnsevdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* nserrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state);
static void testevdunit_testevdset(ae_int_t n,
     double threshold,
     double bithreshold,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_bool* nserrors,
     ae_bool* serrors,
     ae_bool* herrors,
     ae_bool* tderrors,
     ae_bool* sbierrors,
     ae_bool* hbierrors,
     ae_bool* tdbierrors,
     ae_state *_state);





/*************************************************************************
Testing symmetric EVD subroutine
*************************************************************************/
ae_bool testevd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ra;
    ae_int_t n;
    ae_int_t j;
    ae_int_t failc;
    ae_int_t runs;
    double failthreshold;
    double threshold;
    double bithreshold;
    ae_bool waserrors;
    ae_bool nserrors;
    ae_bool serrors;
    ae_bool herrors;
    ae_bool tderrors;
    ae_bool sbierrors;
    ae_bool hbierrors;
    ae_bool tdbierrors;
    ae_bool wfailed;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);

    failthreshold = 0.005;
    threshold = 100000*ae_machineepsilon;
    bithreshold = 1.0E-6;
    nserrors = ae_false;
    serrors = ae_false;
    herrors = ae_false;
    tderrors = ae_false;
    sbierrors = ae_false;
    hbierrors = ae_false;
    tdbierrors = ae_false;
    failc = 0;
    runs = 0;
    
    /*
     * Test problems
     */
    for(n=1; n<=ablasblocksize(&ra, _state); n++)
    {
        testevdunit_testevdset(n, threshold, bithreshold, &failc, &runs, &nserrors, &serrors, &herrors, &tderrors, &sbierrors, &hbierrors, &tdbierrors, _state);
    }
    for(j=2; j<=3; j++)
    {
        for(n=j*ablasblocksize(&ra, _state)-1; n<=j*ablasblocksize(&ra, _state)+1; n++)
        {
            testevdunit_testevdset(n, threshold, bithreshold, &failc, &runs, &nserrors, &serrors, &herrors, &tderrors, &sbierrors, &hbierrors, &tdbierrors, _state);
        }
    }
    
    /*
     * report
     */
    wfailed = ae_fp_greater((double)failc/(double)runs,failthreshold);
    waserrors = ((((((nserrors||serrors)||herrors)||tderrors)||sbierrors)||hbierrors)||tdbierrors)||wfailed;
    if( !silent )
    {
        printf("TESTING EVD UNIT\n");
        printf("NS ERRORS:                               ");
        if( !nserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("S ERRORS:                                ");
        if( !serrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("H ERRORS:                                ");
        if( !herrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("TD ERRORS:                               ");
        if( !tderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SBI ERRORS:                              ");
        if( !sbierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HBI ERRORS:                              ");
        if( !hbierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("TDBI ERRORS:                             ");
        if( !tdbierrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("FAILURE THRESHOLD:                       ");
        if( !wfailed )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Sparse fill
*************************************************************************/
static void testevdunit_rmatrixfillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_double[i][j] = 0;
            }
        }
    }
}


/*************************************************************************
Sparse fill
*************************************************************************/
static void testevdunit_cmatrixfillsparsea(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a->ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_d(0);
            }
        }
    }
}


/*************************************************************************
Copies A to AL (lower half) and AU (upper half), filling unused parts by
random garbage.
*************************************************************************/
static void testevdunit_rmatrixsymmetricsplit(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            al->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            al->ptr.pp_double[j][i] = a->ptr.pp_double[i][j];
            au->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
            au->ptr.pp_double[j][i] = 2*ae_randomreal(_state)-1;
        }
        al->ptr.pp_double[i][i] = a->ptr.pp_double[i][i];
        au->ptr.pp_double[i][i] = a->ptr.pp_double[i][i];
    }
}


/*************************************************************************
Copies A to AL (lower half) and AU (upper half), filling unused parts by
random garbage.
*************************************************************************/
static void testevdunit_cmatrixhermitiansplit(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            al->ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
            al->ptr.pp_complex[j][i] = ae_c_conj(a->ptr.pp_complex[i][j], _state);
            au->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
            au->ptr.pp_complex[j][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
        }
        al->ptr.pp_complex[i][i] = a->ptr.pp_complex[i][i];
        au->ptr.pp_complex[i][i] = a->ptr.pp_complex[i][i];
    }
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testevdunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testevdunit_cunset2d(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testevdunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testevdunit_cunset1d(/* Complex */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_complex[0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Tests Z*Lambda*Z' against tridiag(D,E).
Returns relative error.
*************************************************************************/
static double testevdunit_tdtestproduct(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double mx;
    double result;


    result = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate V = A[i,j], A = Z*Lambda*Z'
             */
            v = 0;
            for(k=0; k<=n-1; k++)
            {
                v = v+z->ptr.pp_double[i][k]*lambdav->ptr.p_double[k]*z->ptr.pp_double[j][k];
            }
            
            /*
             * Compare
             */
            if( ae_iabs(i-j, _state)==0 )
            {
                result = ae_maxreal(result, ae_fabs(v-d->ptr.p_double[i], _state), _state);
            }
            if( ae_iabs(i-j, _state)==1 )
            {
                result = ae_maxreal(result, ae_fabs(v-e->ptr.p_double[ae_minint(i, j, _state)], _state), _state);
            }
            if( ae_iabs(i-j, _state)>1 )
            {
                result = ae_maxreal(result, ae_fabs(v, _state), _state);
            }
        }
    }
    mx = 0;
    for(i=0; i<=n-1; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(d->ptr.p_double[i], _state), _state);
    }
    for(i=0; i<=n-2; i++)
    {
        mx = ae_maxreal(mx, ae_fabs(e->ptr.p_double[i], _state), _state);
    }
    if( ae_fp_eq(mx,0) )
    {
        mx = 1;
    }
    result = result/mx;
    return result;
}


/*************************************************************************
Tests Z*Lambda*Z' against A
Returns relative error.
*************************************************************************/
static double testevdunit_testproduct(/* Real    */ ae_matrix* a,
     ae_int_t n,
     /* Real    */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double mx;
    double result;


    result = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate V = A[i,j], A = Z*Lambda*Z'
             */
            v = 0;
            for(k=0; k<=n-1; k++)
            {
                v = v+z->ptr.pp_double[i][k]*lambdav->ptr.p_double[k]*z->ptr.pp_double[j][k];
            }
            
            /*
             * Compare
             */
            result = ae_maxreal(result, ae_fabs(v-a->ptr.pp_double[i][j], _state), _state);
        }
    }
    mx = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            mx = ae_maxreal(mx, ae_fabs(a->ptr.pp_double[i][j], _state), _state);
        }
    }
    if( ae_fp_eq(mx,0) )
    {
        mx = 1;
    }
    result = result/mx;
    return result;
}


/*************************************************************************
Tests Z*Z' against diag(1...1)
Returns absolute error.
*************************************************************************/
static double testevdunit_testort(/* Real    */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    double result;


    result = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&z->ptr.pp_double[0][i], z->stride, &z->ptr.pp_double[0][j], z->stride, ae_v_len(0,n-1));
            if( i==j )
            {
                v = v-1;
            }
            result = ae_maxreal(result, ae_fabs(v, _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Tests Z*Lambda*Z' against A
Returns relative error.
*************************************************************************/
static double testevdunit_testcproduct(/* Complex */ ae_matrix* a,
     ae_int_t n,
     /* Complex */ ae_matrix* z,
     /* Real    */ ae_vector* lambdav,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_complex v;
    double mx;
    double result;


    result = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate V = A[i,j], A = Z*Lambda*Z'
             */
            v = ae_complex_from_d(0);
            for(k=0; k<=n-1; k++)
            {
                v = ae_c_add(v,ae_c_mul(ae_c_mul_d(z->ptr.pp_complex[i][k],lambdav->ptr.p_double[k]),ae_c_conj(z->ptr.pp_complex[j][k], _state)));
            }
            
            /*
             * Compare
             */
            result = ae_maxreal(result, ae_c_abs(ae_c_sub(v,a->ptr.pp_complex[i][j]), _state), _state);
        }
    }
    mx = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            mx = ae_maxreal(mx, ae_c_abs(a->ptr.pp_complex[i][j], _state), _state);
        }
    }
    if( ae_fp_eq(mx,0) )
    {
        mx = 1;
    }
    result = result/mx;
    return result;
}


/*************************************************************************
Tests Z*Z' against diag(1...1)
Returns absolute error.
*************************************************************************/
static double testevdunit_testcort(/* Complex */ ae_matrix* z,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    double result;


    result = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&z->ptr.pp_complex[0][i], z->stride, "N", &z->ptr.pp_complex[0][j], z->stride, "Conj", ae_v_len(0,n-1));
            if( i==j )
            {
                v = ae_c_sub_d(v,1);
            }
            result = ae_maxreal(result, ae_c_abs(v, _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Tests SEVD problem
*************************************************************************/
static void testevdunit_testsevdproblem(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test simple EVD: values and full vectors, lower A
     */
    testevdunit_unset1d(&lambdaref, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(al, n, 1, ae_false, &lambdaref, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *serrors = *serrors||ae_fp_greater(testevdunit_testproduct(a, n, &z, &lambdaref, _state),threshold);
    *serrors = *serrors||ae_fp_greater(testevdunit_testort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdaref.ptr.p_double[i+1],lambdaref.ptr.p_double[i]) )
        {
            *serrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values and full vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(au, n, 1, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *serrors = *serrors||ae_fp_greater(testevdunit_testproduct(a, n, &z, &lambdav, _state),threshold);
    *serrors = *serrors||ae_fp_greater(testevdunit_testort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdav.ptr.p_double[i+1],lambdav.ptr.p_double[i]) )
        {
            *serrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values only, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(al, n, 0, ae_false, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    
    /*
     * Test simple EVD: values only, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevd(au, n, 0, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests SEVD problem
*************************************************************************/
static void testevdunit_testhevdproblem(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * Test simple EVD: values and full vectors, lower A
     */
    testevdunit_unset1d(&lambdaref, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(al, n, 1, ae_false, &lambdaref, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *herrors = *herrors||ae_fp_greater(testevdunit_testcproduct(a, n, &z, &lambdaref, _state),threshold);
    *herrors = *herrors||ae_fp_greater(testevdunit_testcort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdaref.ptr.p_double[i+1],lambdaref.ptr.p_double[i]) )
        {
            *herrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values and full vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(au, n, 1, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *herrors = *herrors||ae_fp_greater(testevdunit_testcproduct(a, n, &z, &lambdav, _state),threshold);
    *herrors = *herrors||ae_fp_greater(testevdunit_testcort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdav.ptr.p_double[i+1],lambdav.ptr.p_double[i]) )
        {
            *herrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    
    /*
     * Test simple EVD: values only, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(al, n, 0, ae_false, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    
    /*
     * Test simple EVD: values only, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevd(au, n, 0, ae_true, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[i]-lambdaref.ptr.p_double[i], _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem

DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                are solving sparse task with  lots  of  zero  eigenvalues.
                In such cases some tests related to the  eigenvectors  are
                not performed.
*************************************************************************/
static void testevdunit_testsevdbiproblem(/* Real    */ ae_matrix* afull,
     /* Real    */ ae_matrix* al,
     /* Real    */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_matrix ar;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t m;
    ae_int_t i1;
    ae_int_t i2;
    double v;
    double a;
    double b;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ar, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&lambdaref, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    
    /*
     * Reference EVD
     */
    *runs = *runs+1;
    if( !smatrixevd(afull, n, 1, ae_true, &lambdaref, &zref, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Select random interval boundaries.
     * If there are non-distinct eigenvalues at the boundaries,
     * we move indexes further until values splits. It is done to
     * avoid situations where we can't get definite answer.
     */
    i1 = ae_randominteger(n, _state);
    i2 = i1+ae_randominteger(n-i1, _state);
    while(i1>0)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i1-1]-lambdaref.ptr.p_double[i1], _state),10*threshold) )
        {
            break;
        }
        i1 = i1-1;
    }
    while(i2<n-1)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i2+1]-lambdaref.ptr.p_double[i2], _state),10*threshold) )
        {
            break;
        }
        i2 = i2+1;
    }
    
    /*
     * Select A, B
     */
    if( i1>0 )
    {
        a = 0.5*(lambdaref.ptr.p_double[i1]+lambdaref.ptr.p_double[i1-1]);
    }
    else
    {
        a = lambdaref.ptr.p_double[0]-1;
    }
    if( i2<n-1 )
    {
        b = 0.5*(lambdaref.ptr.p_double[i2]+lambdaref.ptr.p_double[i2+1]);
    }
    else
    {
        b = lambdaref.ptr.p_double[n-1]+1;
    }
    
    /*
     * Test interval, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(al, n, 0, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(au, n, 0, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(al, n, 0, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(au, n, 0, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(al, n, 1, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test interval, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdr(au, n, 1, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(al, n, 1, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_unset2d(&z, _state);
    *runs = *runs+1;
    if( !smatrixevdi(au, n, 1, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem

DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                are solving sparse task with  lots  of  zero  eigenvalues.
                In such cases some tests related to the  eigenvectors  are
                not performed.
*************************************************************************/
static void testevdunit_testhevdbiproblem(/* Complex */ ae_matrix* afull,
     /* Complex */ ae_matrix* al,
     /* Complex */ ae_matrix* au,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* herrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_matrix ar;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t m;
    ae_int_t i1;
    ae_int_t i2;
    ae_complex v;
    double a;
    double b;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ar, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_vector_set_length(&lambdaref, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    
    /*
     * Reference EVD
     */
    *runs = *runs+1;
    if( !hmatrixevd(afull, n, 1, ae_true, &lambdaref, &zref, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Select random interval boundaries.
     * If there are non-distinct eigenvalues at the boundaries,
     * we move indexes further until values splits. It is done to
     * avoid situations where we can't get definite answer.
     */
    i1 = ae_randominteger(n, _state);
    i2 = i1+ae_randominteger(n-i1, _state);
    while(i1>0)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i1-1]-lambdaref.ptr.p_double[i1], _state),10*threshold) )
        {
            break;
        }
        i1 = i1-1;
    }
    while(i2<n-1)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i2+1]-lambdaref.ptr.p_double[i2], _state),10*threshold) )
        {
            break;
        }
        i2 = i2+1;
    }
    
    /*
     * Select A, B
     */
    if( i1>0 )
    {
        a = 0.5*(lambdaref.ptr.p_double[i1]+lambdaref.ptr.p_double[i1-1]);
    }
    else
    {
        a = lambdaref.ptr.p_double[0]-1;
    }
    if( i2<n-1 )
    {
        b = 0.5*(lambdaref.ptr.p_double[i2]+lambdaref.ptr.p_double[i2+1]);
    }
    else
    {
        b = lambdaref.ptr.p_double[n-1]+1;
    }
    
    /*
     * Test interval, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(al, n, 0, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(au, n, 0, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(al, n, 0, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(au, n, 0, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(al, n, 1, ae_false, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    
    /*
     * Test interval, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdr(au, n, 1, ae_true, a, b, &m, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, lower A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(al, n, 1, ae_false, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, vectors, upper A
     */
    testevdunit_unset1d(&lambdav, _state);
    testevdunit_cunset2d(&z, _state);
    *runs = *runs+1;
    if( !hmatrixevdi(au, n, 1, ae_true, i1, i2, &lambdav, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *herrors = *herrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        
        /*
         * Distinct eigenvalues, test vectors
         */
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_cdotproduct(&z.ptr.pp_complex[0][j], z.stride, "N", &zref.ptr.pp_complex[0][i1+j], zref.stride, "Conj", ae_v_len(0,n-1));
            v = ae_c_conj(ae_c_div_d(v,ae_c_abs(v, _state)), _state);
            ae_v_cmulc(&z.ptr.pp_complex[0][j], z.stride, ae_v_len(0,n-1), v);
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *herrors = *herrors||ae_fp_greater(ae_c_abs(ae_c_sub(z.ptr.pp_complex[i][j],zref.ptr.pp_complex[i][i1+j]), _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem
*************************************************************************/
static void testevdunit_testtdevdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double threshold,
     ae_bool* tderrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector ee;
    ae_vector lambda2;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_bool wsucc;
    ae_int_t i;
    ae_int_t j;
    double v;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ee, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambda2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&lambdav, n-1+1, _state);
    ae_vector_set_length(&lambda2, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    if( n>1 )
    {
        ae_vector_set_length(&ee, n-2+1, _state);
    }
    
    /*
     * Test simple EVD: values and full vectors
     */
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        ee.ptr.p_double[i] = e->ptr.p_double[i];
    }
    *runs = *runs+1;
    wsucc = smatrixtdevd(&lambdav, &ee, n, 2, &z, _state);
    if( !wsucc )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    *tderrors = *tderrors||ae_fp_greater(testevdunit_tdtestproduct(d, e, n, &z, &lambdav, _state),threshold);
    *tderrors = *tderrors||ae_fp_greater(testevdunit_testort(&z, n, _state),threshold);
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_less(lambdav.ptr.p_double[i+1],lambdav.ptr.p_double[i]) )
        {
            *tderrors = ae_true;
            ae_frame_leave(_state);
            return;
        }
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            zref.ptr.pp_double[i][j] = z.ptr.pp_double[i][j];
        }
    }
    
    /*
     * Test values only variant
     */
    for(i=0; i<=n-1; i++)
    {
        lambda2.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        ee.ptr.p_double[i] = e->ptr.p_double[i];
    }
    *runs = *runs+1;
    wsucc = smatrixtdevd(&lambda2, &ee, n, 0, &z, _state);
    if( !wsucc )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *tderrors = *tderrors||ae_fp_greater(ae_fabs(lambda2.ptr.p_double[i]-lambdav.ptr.p_double[i], _state),threshold);
    }
    
    /*
     * Test multiplication variant
     */
    for(i=0; i<=n-1; i++)
    {
        lambda2.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        ee.ptr.p_double[i] = e->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
        }
    }
    *runs = *runs+1;
    wsucc = smatrixtdevd(&lambda2, &ee, n, 1, &a1, _state);
    if( !wsucc )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *tderrors = *tderrors||ae_fp_greater(ae_fabs(lambda2.ptr.p_double[i]-lambdav.ptr.p_double[i], _state),threshold);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &zref.ptr.pp_double[0][j], zref.stride, ae_v_len(0,n-1));
            
            /*
             * next line is a bit complicated because
             * depending on algorithm used we can get either
             * z or -z as eigenvector. so we compare result
             * with both A*ZRef and -A*ZRef
             */
            *tderrors = *tderrors||(ae_fp_greater(ae_fabs(v-a1.ptr.pp_double[i][j], _state),threshold)&&ae_fp_greater(ae_fabs(v+a1.ptr.pp_double[i][j], _state),threshold));
        }
    }
    
    /*
     * Test first row variant
     */
    for(i=0; i<=n-1; i++)
    {
        lambda2.ptr.p_double[i] = d->ptr.p_double[i];
    }
    for(i=0; i<=n-2; i++)
    {
        ee.ptr.p_double[i] = e->ptr.p_double[i];
    }
    *runs = *runs+1;
    wsucc = smatrixtdevd(&lambda2, &ee, n, 3, &z, _state);
    if( !wsucc )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(i=0; i<=n-1; i++)
    {
        *tderrors = *tderrors||ae_fp_greater(ae_fabs(lambda2.ptr.p_double[i]-lambdav.ptr.p_double[i], _state),threshold);
        
        /*
         * next line is a bit complicated because
         * depending on algorithm used we can get either
         * z or -z as eigenvector. so we compare result
         * with both z and -z
         */
        *tderrors = *tderrors||(ae_fp_greater(ae_fabs(z.ptr.pp_double[0][i]-zref.ptr.pp_double[0][i], _state),threshold)&&ae_fp_greater(ae_fabs(z.ptr.pp_double[0][i]+zref.ptr.pp_double[0][i], _state),threshold));
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests EVD problem

DistVals    -   is True, when eigenvalues are distinct. Is False, when we
                are solving sparse task with  lots  of  zero  eigenvalues.
                In such cases some tests related to the  eigenvectors  are
                not performed.
*************************************************************************/
static void testevdunit_testtdevdbiproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool distvals,
     double threshold,
     ae_bool* serrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector lambdav;
    ae_vector lambdaref;
    ae_matrix z;
    ae_matrix zref;
    ae_matrix a1;
    ae_matrix a2;
    ae_matrix ar;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t m;
    ae_int_t i1;
    ae_int_t i2;
    double v;
    double a;
    double b;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&lambdav, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&lambdaref, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&zref, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ar, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&lambdaref, n-1+1, _state);
    ae_matrix_set_length(&zref, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    
    /*
     * Reference EVD
     */
    ae_vector_set_length(&lambdaref, n, _state);
    ae_v_move(&lambdaref.ptr.p_double[0], 1, &d->ptr.p_double[0], 1, ae_v_len(0,n-1));
    *runs = *runs+1;
    if( !smatrixtdevd(&lambdaref, e, n, 2, &zref, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Select random interval boundaries.
     * If there are non-distinct eigenvalues at the boundaries,
     * we move indexes further until values splits. It is done to
     * avoid situations where we can't get definite answer.
     */
    i1 = ae_randominteger(n, _state);
    i2 = i1+ae_randominteger(n-i1, _state);
    while(i1>0)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i1-1]-lambdaref.ptr.p_double[i1], _state),10*threshold) )
        {
            break;
        }
        i1 = i1-1;
    }
    while(i2<n-1)
    {
        if( ae_fp_greater(ae_fabs(lambdaref.ptr.p_double[i2+1]-lambdaref.ptr.p_double[i2], _state),10*threshold) )
        {
            break;
        }
        i2 = i2+1;
    }
    
    /*
     * Test different combinations
     */
    
    /*
     * Select A, B
     */
    if( i1>0 )
    {
        a = 0.5*(lambdaref.ptr.p_double[i1]+lambdaref.ptr.p_double[i1-1]);
    }
    else
    {
        a = lambdaref.ptr.p_double[0]-1;
    }
    if( i2<n-1 )
    {
        b = 0.5*(lambdaref.ptr.p_double[i2]+lambdaref.ptr.p_double[i2+1]);
    }
    else
    {
        b = lambdaref.ptr.p_double[n-1]+1;
    }
    
    /*
     * Test interval, no vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    *runs = *runs+1;
    if( !smatrixtdevdr(&lambdav, e, n, 0, a, b, &m, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test indexes, no vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    *runs = *runs+1;
    if( !smatrixtdevdi(&lambdav, e, n, 0, i1, i2, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    
    /*
     * Test interval, transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
        }
    }
    *runs = *runs+1;
    if( !smatrixtdevdr(&lambdav, e, n, 1, a, b, &m, &a1, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        ae_matrix_set_length(&ar, n-1+1, m-1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                v = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
                ar.ptr.pp_double[i][j] = v;
            }
        }
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&a1.ptr.pp_double[0][j], a1.stride, &ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(a1.ptr.pp_double[i][j]-ar.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&a1, n-1+1, n-1+1, _state);
    ae_matrix_set_length(&a2, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            a2.ptr.pp_double[i][j] = a1.ptr.pp_double[i][j];
        }
    }
    *runs = *runs+1;
    if( !smatrixtdevdi(&lambdav, e, n, 1, i1, i2, &a1, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        ae_matrix_set_length(&ar, n-1+1, m-1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                v = ae_v_dotproduct(&a2.ptr.pp_double[i][0], 1, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
                ar.ptr.pp_double[i][j] = v;
            }
        }
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&a1.ptr.pp_double[0][j], a1.stride, &ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&ar.ptr.pp_double[0][j], ar.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(a1.ptr.pp_double[i][j]-ar.ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    
    /*
     * Test interval, do not transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&z, 0+1, 0+1, _state);
    *runs = *runs+1;
    if( !smatrixtdevdr(&lambdav, e, n, 2, a, b, &m, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    if( m!=i2-i1+1 )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    
    /*
     * Test indexes, do not transform vectors
     */
    ae_vector_set_length(&lambdav, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        lambdav.ptr.p_double[i] = d->ptr.p_double[i];
    }
    ae_matrix_set_length(&z, 0+1, 0+1, _state);
    *runs = *runs+1;
    if( !smatrixtdevdi(&lambdav, e, n, 2, i1, i2, &z, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    m = i2-i1+1;
    for(k=0; k<=m-1; k++)
    {
        *serrors = *serrors||ae_fp_greater(ae_fabs(lambdav.ptr.p_double[k]-lambdaref.ptr.p_double[i1+k], _state),threshold);
    }
    if( distvals )
    {
        for(j=0; j<=m-1; j++)
        {
            v = ae_v_dotproduct(&z.ptr.pp_double[0][j], z.stride, &zref.ptr.pp_double[0][i1+j], zref.stride, ae_v_len(0,n-1));
            if( ae_fp_less(v,0) )
            {
                ae_v_muld(&z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1), -1);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                *serrors = *serrors||ae_fp_greater(ae_fabs(z.ptr.pp_double[i][j]-zref.ptr.pp_double[i][i1+j], _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Non-symmetric problem
*************************************************************************/
static void testevdunit_testnsevdproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double threshold,
     ae_bool* nserrors,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_state *_state)
{
    ae_frame _frame_block;
    double mx;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t vjob;
    ae_bool needl;
    ae_bool needr;
    ae_vector wr0;
    ae_vector wi0;
    ae_vector wr1;
    ae_vector wi1;
    ae_vector wr0s;
    ae_vector wi0s;
    ae_vector wr1s;
    ae_vector wi1s;
    ae_matrix vl;
    ae_matrix vr;
    ae_vector vec1r;
    ae_vector vec1i;
    ae_vector vec2r;
    ae_vector vec2i;
    ae_vector vec3r;
    ae_vector vec3i;
    double curwr;
    double curwi;
    double vt;
    double tmp;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&wr0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wr0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi0s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wr1s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wi1s, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vr, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec1r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec1i, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec2r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec2i, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec3r, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vec3i, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&vec1r, n-1+1, _state);
    ae_vector_set_length(&vec2r, n-1+1, _state);
    ae_vector_set_length(&vec3r, n-1+1, _state);
    ae_vector_set_length(&vec1i, n-1+1, _state);
    ae_vector_set_length(&vec2i, n-1+1, _state);
    ae_vector_set_length(&vec3i, n-1+1, _state);
    ae_vector_set_length(&wr0s, n-1+1, _state);
    ae_vector_set_length(&wr1s, n-1+1, _state);
    ae_vector_set_length(&wi0s, n-1+1, _state);
    ae_vector_set_length(&wi1s, n-1+1, _state);
    mx = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j], _state),mx) )
            {
                mx = ae_fabs(a->ptr.pp_double[i][j], _state);
            }
        }
    }
    if( ae_fp_eq(mx,0) )
    {
        mx = 1;
    }
    
    /*
     * Load values-only
     */
    *runs = *runs+1;
    if( !rmatrixevd(a, n, 0, &wr0, &wi0, &vl, &vr, _state) )
    {
        *failc = *failc+1;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Test different jobs
     */
    for(vjob=1; vjob<=3; vjob++)
    {
        needr = vjob==1||vjob==3;
        needl = vjob==2||vjob==3;
        *runs = *runs+1;
        if( !rmatrixevd(a, n, vjob, &wr1, &wi1, &vl, &vr, _state) )
        {
            *failc = *failc+1;
            ae_frame_leave(_state);
            return;
        }
        
        /*
         * Test values:
         * 1. sort by real part
         * 2. test
         */
        ae_v_move(&wr0s.ptr.p_double[0], 1, &wr0.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&wi0s.ptr.p_double[0], 1, &wi0.ptr.p_double[0], 1, ae_v_len(0,n-1));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-2-i; j++)
            {
                if( ae_fp_greater(wr0s.ptr.p_double[j],wr0s.ptr.p_double[j+1]) )
                {
                    tmp = wr0s.ptr.p_double[j];
                    wr0s.ptr.p_double[j] = wr0s.ptr.p_double[j+1];
                    wr0s.ptr.p_double[j+1] = tmp;
                    tmp = wi0s.ptr.p_double[j];
                    wi0s.ptr.p_double[j] = wi0s.ptr.p_double[j+1];
                    wi0s.ptr.p_double[j+1] = tmp;
                }
            }
        }
        ae_v_move(&wr1s.ptr.p_double[0], 1, &wr1.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&wi1s.ptr.p_double[0], 1, &wi1.ptr.p_double[0], 1, ae_v_len(0,n-1));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-2-i; j++)
            {
                if( ae_fp_greater(wr1s.ptr.p_double[j],wr1s.ptr.p_double[j+1]) )
                {
                    tmp = wr1s.ptr.p_double[j];
                    wr1s.ptr.p_double[j] = wr1s.ptr.p_double[j+1];
                    wr1s.ptr.p_double[j+1] = tmp;
                    tmp = wi1s.ptr.p_double[j];
                    wi1s.ptr.p_double[j] = wi1s.ptr.p_double[j+1];
                    wi1s.ptr.p_double[j+1] = tmp;
                }
            }
        }
        for(i=0; i<=n-1; i++)
        {
            *nserrors = *nserrors||ae_fp_greater(ae_fabs(wr0s.ptr.p_double[i]-wr1s.ptr.p_double[i], _state),threshold);
            *nserrors = *nserrors||ae_fp_greater(ae_fabs(wi0s.ptr.p_double[i]-wi1s.ptr.p_double[i], _state),threshold);
        }
        
        /*
         * Test right vectors
         */
        if( needr )
        {
            k = 0;
            while(k<=n-1)
            {
                if( ae_fp_eq(wi1.ptr.p_double[k],0) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k], vr.stride, ae_v_len(0,n-1));
                    for(i=0; i<=n-1; i++)
                    {
                        vec1i.ptr.p_double[i] = 0;
                    }
                    curwr = wr1.ptr.p_double[k];
                    curwi = 0;
                }
                if( ae_fp_greater(wi1.ptr.p_double[k],0) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k], vr.stride, ae_v_len(0,n-1));
                    ae_v_move(&vec1i.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k+1], vr.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                if( ae_fp_less(wi1.ptr.p_double[k],0) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k-1], vr.stride, ae_v_len(0,n-1));
                    ae_v_moveneg(&vec1i.ptr.p_double[0], 1, &vr.ptr.pp_double[0][k], vr.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                for(i=0; i<=n-1; i++)
                {
                    vt = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    vec2r.ptr.p_double[i] = vt;
                    vt = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    vec2i.ptr.p_double[i] = vt;
                }
                ae_v_moved(&vec3r.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwr);
                ae_v_subd(&vec3r.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_moved(&vec3i.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_addd(&vec3i.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), curwr);
                for(i=0; i<=n-1; i++)
                {
                    *nserrors = *nserrors||ae_fp_greater(ae_fabs(vec2r.ptr.p_double[i]-vec3r.ptr.p_double[i], _state),threshold);
                    *nserrors = *nserrors||ae_fp_greater(ae_fabs(vec2i.ptr.p_double[i]-vec3i.ptr.p_double[i], _state),threshold);
                }
                k = k+1;
            }
        }
        
        /*
         * Test left vectors
         */
        if( needl )
        {
            k = 0;
            while(k<=n-1)
            {
                if( ae_fp_eq(wi1.ptr.p_double[k],0) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k], vl.stride, ae_v_len(0,n-1));
                    for(i=0; i<=n-1; i++)
                    {
                        vec1i.ptr.p_double[i] = 0;
                    }
                    curwr = wr1.ptr.p_double[k];
                    curwi = 0;
                }
                if( ae_fp_greater(wi1.ptr.p_double[k],0) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k], vl.stride, ae_v_len(0,n-1));
                    ae_v_move(&vec1i.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k+1], vl.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                if( ae_fp_less(wi1.ptr.p_double[k],0) )
                {
                    ae_v_move(&vec1r.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k-1], vl.stride, ae_v_len(0,n-1));
                    ae_v_moveneg(&vec1i.ptr.p_double[0], 1, &vl.ptr.pp_double[0][k], vl.stride, ae_v_len(0,n-1));
                    curwr = wr1.ptr.p_double[k];
                    curwi = wi1.ptr.p_double[k];
                }
                for(j=0; j<=n-1; j++)
                {
                    vt = ae_v_dotproduct(&vec1r.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
                    vec2r.ptr.p_double[j] = vt;
                    vt = ae_v_dotproduct(&vec1i.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
                    vec2i.ptr.p_double[j] = -vt;
                }
                ae_v_moved(&vec3r.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwr);
                ae_v_addd(&vec3r.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_moved(&vec3i.ptr.p_double[0], 1, &vec1r.ptr.p_double[0], 1, ae_v_len(0,n-1), curwi);
                ae_v_addd(&vec3i.ptr.p_double[0], 1, &vec1i.ptr.p_double[0], 1, ae_v_len(0,n-1), -curwr);
                for(i=0; i<=n-1; i++)
                {
                    *nserrors = *nserrors||ae_fp_greater(ae_fabs(vec2r.ptr.p_double[i]-vec3r.ptr.p_double[i], _state),threshold);
                    *nserrors = *nserrors||ae_fp_greater(ae_fabs(vec2i.ptr.p_double[i]-vec3i.ptr.p_double[i], _state),threshold);
                }
                k = k+1;
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Testing EVD subroutines for one N

NOTES:
* BIThreshold is a threshold for bisection-and-inverse-iteration subroutines.
  special threshold is needed because these subroutines may have much more
  larger error than QR-based algorithms.
*************************************************************************/
static void testevdunit_testevdset(ae_int_t n,
     double threshold,
     double bithreshold,
     ae_int_t* failc,
     ae_int_t* runs,
     ae_bool* nserrors,
     ae_bool* serrors,
     ae_bool* herrors,
     ae_bool* tderrors,
     ae_bool* sbierrors,
     ae_bool* hbierrors,
     ae_bool* tdbierrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ra;
    ae_matrix ral;
    ae_matrix rau;
    ae_matrix ca;
    ae_matrix cal;
    ae_matrix cau;
    ae_vector d;
    ae_vector e;
    ae_int_t i;
    ae_int_t j;
    ae_int_t mkind;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ral, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rau, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cal, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cau, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);

    
    /*
     * Test symmetric problems
     */
    ae_matrix_set_length(&ra, n, n, _state);
    ae_matrix_set_length(&ral, n, n, _state);
    ae_matrix_set_length(&rau, n, n, _state);
    ae_matrix_set_length(&ca, n, n, _state);
    ae_matrix_set_length(&cal, n, n, _state);
    ae_matrix_set_length(&cau, n, n, _state);
    
    /*
     * Zero matrices
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 0;
            ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    testevdunit_testsevdbiproblem(&ra, &ral, &rau, n, ae_false, bithreshold, sbierrors, failc, runs, _state);
    testevdunit_testhevdbiproblem(&ca, &cal, &cau, n, ae_false, bithreshold, hbierrors, failc, runs, _state);
    
    /*
     * Random matrix
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
            ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
        }
        ra.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
        ca.ptr.pp_complex[i][i] = ae_complex_from_d(2*ae_randomreal(_state)-1);
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    
    /*
     * Random diagonally dominant matrix with distinct eigenvalues
     */
    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 0.1*(2*ae_randomreal(_state)-1)/n;
            ca.ptr.pp_complex[i][j].x = 0.1*(2*ae_randomreal(_state)-1)/n;
            ca.ptr.pp_complex[i][j].y = 0.1*(2*ae_randomreal(_state)-1)/n;
            ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
            ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
        }
        ra.ptr.pp_double[i][i] = 0.1*(2*ae_randomreal(_state)-1)+i;
        ca.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*(2*ae_randomreal(_state)-1)+i);
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    testevdunit_testsevdbiproblem(&ra, &ral, &rau, n, ae_true, bithreshold, sbierrors, failc, runs, _state);
    testevdunit_testhevdbiproblem(&ca, &cal, &cau, n, ae_true, bithreshold, hbierrors, failc, runs, _state);
    
    /*
     * Sparse matrices
     */
    testevdunit_rmatrixfillsparsea(&ra, n, n, 0.995, _state);
    testevdunit_cmatrixfillsparsea(&ca, n, n, 0.995, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=i+1; j<=n-1; j++)
        {
            ra.ptr.pp_double[j][i] = ra.ptr.pp_double[i][j];
            ca.ptr.pp_complex[j][i] = ae_c_conj(ca.ptr.pp_complex[i][j], _state);
        }
        ca.ptr.pp_complex[i][i].y = 0;
    }
    testevdunit_rmatrixsymmetricsplit(&ra, n, &ral, &rau, _state);
    testevdunit_cmatrixhermitiansplit(&ca, n, &cal, &cau, _state);
    testevdunit_testsevdproblem(&ra, &ral, &rau, n, threshold, serrors, failc, runs, _state);
    testevdunit_testhevdproblem(&ca, &cal, &cau, n, threshold, herrors, failc, runs, _state);
    testevdunit_testsevdbiproblem(&ra, &ral, &rau, n, ae_false, bithreshold, sbierrors, failc, runs, _state);
    testevdunit_testhevdbiproblem(&ca, &cal, &cau, n, ae_false, bithreshold, hbierrors, failc, runs, _state);
    
    /*
     * testing tridiagonal problems
     */
    for(mkind=0; mkind<=7; mkind++)
    {
        ae_vector_set_length(&d, n, _state);
        if( n>1 )
        {
            ae_vector_set_length(&e, n-1, _state);
        }
        if( mkind==0 )
        {
            
            /*
             * Zero matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 0;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 0;
            }
        }
        if( mkind==1 )
        {
            
            /*
             * Diagonal matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 0;
            }
        }
        if( mkind==2 )
        {
            
            /*
             * Off-diagonal matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 0;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        if( mkind==3 )
        {
            
            /*
             * Dense matrix with blocks
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            j = 1;
            i = 2;
            while(j<=n-2)
            {
                e.ptr.p_double[j] = 0;
                j = j+i;
                i = i+1;
            }
        }
        if( mkind==4 )
        {
            
            /*
             * dense matrix
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
        }
        if( mkind==5 )
        {
            
            /*
             * Diagonal matrix with distinct eigenvalues
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1)+i;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 0;
            }
        }
        if( mkind==6 )
        {
            
            /*
             * Off-diagonal matrix with distinct eigenvalues
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 0;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1)+i+1;
            }
        }
        if( mkind==7 )
        {
            
            /*
             * dense matrix with distinct eigenvalues
             */
            for(i=0; i<=n-1; i++)
            {
                d.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1)+i+1;
            }
            for(i=0; i<=n-2; i++)
            {
                e.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
        }
        testevdunit_testtdevdproblem(&d, &e, n, threshold, tderrors, failc, runs, _state);
        testevdunit_testtdevdbiproblem(&d, &e, n, (mkind==5||mkind==6)||mkind==7, bithreshold, tdbierrors, failc, runs, _state);
    }
    
    /*
     * Test non-symmetric problems
     */
    
    /*
     * Test non-symmetric problems: zero, random, sparse matrices.
     */
    ae_matrix_set_length(&ra, n, n, _state);
    ae_matrix_set_length(&ca, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 0;
            ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
    testevdunit_testnsevdproblem(&ra, n, threshold, nserrors, failc, runs, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            ra.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
            ca.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
        }
    }
    testevdunit_testnsevdproblem(&ra, n, threshold, nserrors, failc, runs, _state);
    testevdunit_rmatrixfillsparsea(&ra, n, n, 0.995, _state);
    testevdunit_cmatrixfillsparsea(&ca, n, n, 0.995, _state);
    testevdunit_testnsevdproblem(&ra, n, threshold, nserrors, failc, runs, _state);
    ae_frame_leave(_state);
}



static ae_int_t testmatgenunit_maxsvditerations = 60;
static void testmatgenunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state);
static void testmatgenunit_unset2dc(/* Complex */ ae_matrix* a,
     ae_state *_state);
static ae_bool testmatgenunit_isspd(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_state *_state);
static ae_bool testmatgenunit_ishpd(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static double testmatgenunit_svdcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static ae_bool testmatgenunit_obsoletesvddecomposition(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* v,
     ae_state *_state);
static double testmatgenunit_extsign(double a, double b, ae_state *_state);
static double testmatgenunit_mymax(double a, double b, ae_state *_state);
static double testmatgenunit_pythag(double a, double b, ae_state *_state);





ae_bool testmatgen(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix b;
    ae_matrix u;
    ae_matrix v;
    ae_matrix ca;
    ae_matrix cb;
    ae_matrix r1;
    ae_matrix r2;
    ae_matrix c1;
    ae_matrix c2;
    ae_vector w;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    double cond;
    double threshold;
    double vt;
    ae_complex ct;
    double minw;
    double maxw;
    ae_bool serr;
    ae_bool herr;
    ae_bool spderr;
    ae_bool hpderr;
    ae_bool rerr;
    ae_bool cerr;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cb, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&r1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c1, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&c2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    rerr = ae_false;
    cerr = ae_false;
    serr = ae_false;
    herr = ae_false;
    spderr = ae_false;
    hpderr = ae_false;
    waserrors = ae_false;
    maxn = 20;
    passcount = 15;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Testing orthogonal
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            ae_matrix_set_length(&r1, n-1+1, 2*n-1+1, _state);
            ae_matrix_set_length(&r2, 2*n-1+1, n-1+1, _state);
            ae_matrix_set_length(&c1, n-1+1, 2*n-1+1, _state);
            ae_matrix_set_length(&c2, 2*n-1+1, n-1+1, _state);
            
            /*
             * Random orthogonal, real
             */
            testmatgenunit_unset2d(&a, _state);
            testmatgenunit_unset2d(&b, _state);
            rmatrixrndorthogonal(n, &a, _state);
            rmatrixrndorthogonal(n, &b, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    vt = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &a.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    vt = ae_v_dotproduct(&b.ptr.pp_double[i][0], 1, &b.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        rerr = rerr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
            
            /*
             * Random orthogonal, complex
             */
            testmatgenunit_unset2dc(&ca, _state);
            testmatgenunit_unset2dc(&cb, _state);
            cmatrixrndorthogonal(n, &ca, _state);
            cmatrixrndorthogonal(n, &cb, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    ct = ae_v_cdotproduct(&ca.ptr.pp_complex[i][0], 1, "N", &ca.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    ct = ae_v_cdotproduct(&cb.ptr.pp_complex[i][0], 1, "N", &cb.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        cerr = cerr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                    }
                }
            }
            
            /*
             * From the right real tests:
             * 1. E*Q is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. (E E)'*Q = (Q' Q')' (correct handling of non-square matrices)
             */
            testmatgenunit_unset2d(&a, _state);
            testmatgenunit_unset2d(&b, _state);
            ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&b, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0;
                    b.ptr.pp_double[i][j] = 0;
                }
                a.ptr.pp_double[i][i] = 1;
                b.ptr.pp_double[i][i] = 1;
            }
            rmatrixrndorthogonalfromtheright(&a, n, n, _state);
            rmatrixrndorthogonalfromtheright(&b, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    vt = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &a.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    vt = ae_v_dotproduct(&b.ptr.pp_double[i][0], 1, &b.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        rerr = rerr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    r2.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    r2.ptr.pp_double[i+n][j] = r2.ptr.pp_double[i][j];
                }
            }
            rmatrixrndorthogonalfromtheright(&r2, 2*n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rerr = rerr||ae_fp_greater(ae_fabs(r2.ptr.pp_double[i+n][j]-r2.ptr.pp_double[i][j], _state),threshold);
                }
            }
            
            /*
             * From the left real tests:
             * 1. Q*E is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. Q*(E E) = (Q Q) (correct handling of non-square matrices)
             */
            testmatgenunit_unset2d(&a, _state);
            testmatgenunit_unset2d(&b, _state);
            ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&b, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0;
                    b.ptr.pp_double[i][j] = 0;
                }
                a.ptr.pp_double[i][i] = 1;
                b.ptr.pp_double[i][i] = 1;
            }
            rmatrixrndorthogonalfromtheleft(&a, n, n, _state);
            rmatrixrndorthogonalfromtheleft(&b, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    vt = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &a.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    vt = ae_v_dotproduct(&b.ptr.pp_double[i][0], 1, &b.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                    if( i==j )
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt-1, _state),threshold);
                    }
                    else
                    {
                        rerr = rerr||ae_fp_greater(ae_fabs(vt, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    if( n>=2 )
                    {
                        rerr = rerr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    r1.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    r1.ptr.pp_double[i][j+n] = r1.ptr.pp_double[i][j];
                }
            }
            rmatrixrndorthogonalfromtheleft(&r1, n, 2*n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    rerr = rerr||ae_fp_greater(ae_fabs(r1.ptr.pp_double[i][j]-r1.ptr.pp_double[i][j+n], _state),threshold);
                }
            }
            
            /*
             * From the right complex tests:
             * 1. E*Q is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. (E E)'*Q = (Q' Q')' (correct handling of non-square matrices)
             */
            testmatgenunit_unset2dc(&ca, _state);
            testmatgenunit_unset2dc(&cb, _state);
            ae_matrix_set_length(&ca, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&cb, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                    cb.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                }
                ca.ptr.pp_complex[i][i] = ae_complex_from_d(1);
                cb.ptr.pp_complex[i][i] = ae_complex_from_d(1);
            }
            cmatrixrndorthogonalfromtheright(&ca, n, n, _state);
            cmatrixrndorthogonalfromtheright(&cb, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    ct = ae_v_cdotproduct(&ca.ptr.pp_complex[i][0], 1, "N", &ca.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    ct = ae_v_cdotproduct(&cb.ptr.pp_complex[i][0], 1, "N", &cb.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    cerr = cerr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c2.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                    c2.ptr.pp_complex[i+n][j] = c2.ptr.pp_complex[i][j];
                }
            }
            cmatrixrndorthogonalfromtheright(&c2, 2*n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub(c2.ptr.pp_complex[i+n][j],c2.ptr.pp_complex[i][j]), _state),threshold);
                }
            }
            
            /*
             * From the left complex tests:
             * 1. Q*E is orthogonal
             * 2. Q1<>Q2 (routine result is changing)
             * 3. Q*(E E) = (Q Q) (correct handling of non-square matrices)
             */
            testmatgenunit_unset2dc(&ca, _state);
            testmatgenunit_unset2dc(&cb, _state);
            ae_matrix_set_length(&ca, n-1+1, n-1+1, _state);
            ae_matrix_set_length(&cb, n-1+1, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                    cb.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                }
                ca.ptr.pp_complex[i][i] = ae_complex_from_d(1);
                cb.ptr.pp_complex[i][i] = ae_complex_from_d(1);
            }
            cmatrixrndorthogonalfromtheleft(&ca, n, n, _state);
            cmatrixrndorthogonalfromtheleft(&cb, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    
                    /*
                     * orthogonality test
                     */
                    ct = ae_v_cdotproduct(&ca.ptr.pp_complex[i][0], 1, "N", &ca.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    ct = ae_v_cdotproduct(&cb.ptr.pp_complex[i][0], 1, "N", &cb.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,n-1));
                    if( i==j )
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub_d(ct,1), _state),threshold);
                    }
                    else
                    {
                        cerr = cerr||ae_fp_greater(ae_c_abs(ct, _state),threshold);
                    }
                    
                    /*
                     * test for difference in A and B
                     */
                    cerr = cerr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    c1.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                    c1.ptr.pp_complex[i][j+n] = c1.ptr.pp_complex[i][j];
                }
            }
            cmatrixrndorthogonalfromtheleft(&c1, n, 2*n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    cerr = cerr||ae_fp_greater(ae_c_abs(ae_c_sub(c1.ptr.pp_complex[i][j],c1.ptr.pp_complex[i][j+n]), _state),threshold);
                }
            }
        }
    }
    
    /*
     * Testing GCond
     */
    for(n=2; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * real test
             */
            testmatgenunit_unset2d(&a, _state);
            cond = ae_exp(ae_log(1000, _state)*ae_randomreal(_state), _state);
            rmatrixrndcond(n, cond, &a, _state);
            ae_matrix_set_length(&b, n+1, n+1, _state);
            for(i=1; i<=n; i++)
            {
                for(j=1; j<=n; j++)
                {
                    b.ptr.pp_double[i][j] = a.ptr.pp_double[i-1][j-1];
                }
            }
            if( testmatgenunit_obsoletesvddecomposition(&b, n, n, &w, &v, _state) )
            {
                maxw = w.ptr.p_double[1];
                minw = w.ptr.p_double[1];
                for(i=2; i<=n; i++)
                {
                    if( ae_fp_greater(w.ptr.p_double[i],maxw) )
                    {
                        maxw = w.ptr.p_double[i];
                    }
                    if( ae_fp_less(w.ptr.p_double[i],minw) )
                    {
                        minw = w.ptr.p_double[i];
                    }
                }
                vt = maxw/minw/cond;
                if( ae_fp_greater(ae_fabs(ae_log(vt, _state), _state),ae_log(1+threshold, _state)) )
                {
                    rerr = ae_true;
                }
            }
        }
    }
    
    /*
     * Symmetric/SPD
     * N = 2 .. 30
     */
    for(n=2; n<=maxn; n++)
    {
        
        /*
         * SPD matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate A
             */
            testmatgenunit_unset2d(&a, _state);
            cond = ae_exp(ae_log(1000, _state)*ae_randomreal(_state), _state);
            spdmatrixrndcond(n, cond, &a, _state);
            
            /*
             * test condition number
             */
            spderr = spderr||ae_fp_greater(testmatgenunit_svdcond(&a, n, _state)/cond-1,threshold);
            
            /*
             * test SPD
             */
            spderr = spderr||!testmatgenunit_isspd(&a, n, ae_true, _state);
            
            /*
             * test that A is symmetic
             */
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    spderr = spderr||ae_fp_greater(ae_fabs(a.ptr.pp_double[i][j]-a.ptr.pp_double[j][i], _state),threshold);
                }
            }
            
            /*
             * test for difference between A and B (subsequent matrix)
             */
            testmatgenunit_unset2d(&b, _state);
            spdmatrixrndcond(n, cond, &b, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        spderr = spderr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
        }
        
        /*
         * HPD matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate A
             */
            testmatgenunit_unset2dc(&ca, _state);
            cond = ae_exp(ae_log(1000, _state)*ae_randomreal(_state), _state);
            hpdmatrixrndcond(n, cond, &ca, _state);
            
            /*
             * test HPD
             */
            hpderr = hpderr||!testmatgenunit_ishpd(&ca, n, _state);
            
            /*
             * test that A is Hermitian
             */
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    hpderr = hpderr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],ae_c_conj(ca.ptr.pp_complex[j][i], _state)), _state),threshold);
                }
            }
            
            /*
             * test for difference between A and B (subsequent matrix)
             */
            testmatgenunit_unset2dc(&cb, _state);
            hpdmatrixrndcond(n, cond, &cb, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        hpderr = hpderr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                    }
                }
            }
        }
        
        /*
         * Symmetric matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * test condition number
             */
            testmatgenunit_unset2d(&a, _state);
            cond = ae_exp(ae_log(1000, _state)*ae_randomreal(_state), _state);
            smatrixrndcond(n, cond, &a, _state);
            serr = serr||ae_fp_greater(testmatgenunit_svdcond(&a, n, _state)/cond-1,threshold);
            
            /*
             * test for difference between A and B
             */
            testmatgenunit_unset2d(&b, _state);
            smatrixrndcond(n, cond, &b, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        serr = serr||ae_fp_eq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                    }
                }
            }
        }
        
        /*
         * Hermitian matrices
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Generate A
             */
            testmatgenunit_unset2dc(&ca, _state);
            cond = ae_exp(ae_log(1000, _state)*ae_randomreal(_state), _state);
            hmatrixrndcond(n, cond, &ca, _state);
            
            /*
             * test that A is Hermitian
             */
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    herr = herr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],ae_c_conj(ca.ptr.pp_complex[j][i], _state)), _state),threshold);
                }
            }
            
            /*
             * test for difference between A and B (subsequent matrix)
             */
            testmatgenunit_unset2dc(&cb, _state);
            hmatrixrndcond(n, cond, &cb, _state);
            if( n>=2 )
            {
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        herr = herr||ae_c_eq(ca.ptr.pp_complex[i][j],cb.ptr.pp_complex[i][j]);
                    }
                }
            }
        }
    }
    
    /*
     * report
     */
    waserrors = ((((rerr||cerr)||serr)||spderr)||herr)||hpderr;
    if( !silent )
    {
        printf("TESTING MATRIX GENERATOR\n");
        printf("REAL TEST:                               ");
        if( !rerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX TEST:                            ");
        if( !cerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SYMMETRIC TEST:                          ");
        if( !serr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HERMITIAN TEST:                          ");
        if( !herr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPD TEST:                                ");
        if( !spderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HPD TEST:                                ");
        if( !hpderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testmatgenunit_unset2d(/* Real    */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testmatgenunit_unset2dc(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Test whether matrix is SPD
*************************************************************************/
static ae_bool testmatgenunit_isspd(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_int_t i;
    ae_int_t j;
    double ajj;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;

    
    /*
     *     Test the input parameters.
     */
    ae_assert(n>=0, "Error in SMatrixCholesky: incorrect function arguments", _state);
    
    /*
     *     Quick return if possible
     */
    result = ae_true;
    if( n<=0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    if( isupper )
    {
        
        /*
         * Compute the Cholesky factorization A = U'*U.
         */
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Compute U(J,J) and test for non-positive-definiteness.
             */
            v = ae_v_dotproduct(&a->ptr.pp_double[0][j], a->stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
            ajj = a->ptr.pp_double[j][j]-v;
            if( ae_fp_less_eq(ajj,0) )
            {
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            ajj = ae_sqrt(ajj, _state);
            a->ptr.pp_double[j][j] = ajj;
            
            /*
             * Compute elements J+1:N of row J.
             */
            if( j<n-1 )
            {
                for(i=j+1; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[0][i], a->stride, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                    a->ptr.pp_double[j][i] = a->ptr.pp_double[j][i]-v;
                }
                v = 1/ajj;
                ae_v_muld(&a->ptr.pp_double[j][j+1], 1, ae_v_len(j+1,n-1), v);
            }
        }
    }
    else
    {
        
        /*
         * Compute the Cholesky factorization A = L*L'.
         */
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Compute L(J,J) and test for non-positive-definiteness.
             */
            v = ae_v_dotproduct(&a->ptr.pp_double[j][0], 1, &a->ptr.pp_double[j][0], 1, ae_v_len(0,j-1));
            ajj = a->ptr.pp_double[j][j]-v;
            if( ae_fp_less_eq(ajj,0) )
            {
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            ajj = ae_sqrt(ajj, _state);
            a->ptr.pp_double[j][j] = ajj;
            
            /*
             * Compute elements J+1:N of column J.
             */
            if( j<n-1 )
            {
                for(i=j+1; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &a->ptr.pp_double[j][0], 1, ae_v_len(0,j-1));
                    a->ptr.pp_double[i][j] = a->ptr.pp_double[i][j]-v;
                }
                v = 1/ajj;
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), v);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests whether A is HPD
*************************************************************************/
static ae_bool testmatgenunit_ishpd(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_int_t j;
    double ajj;
    ae_complex v;
    double r;
    ae_vector t;
    ae_vector t2;
    ae_vector t3;
    ae_int_t i;
    ae_matrix a1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;
    ae_vector_init(&t, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&t2, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&t3, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&a1, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_vector_set_length(&t, n-1+1, _state);
    ae_vector_set_length(&t2, n-1+1, _state);
    ae_vector_set_length(&t3, n-1+1, _state);
    result = ae_true;
    
    /*
     * Compute the Cholesky factorization A = U'*U.
     */
    for(j=0; j<=n-1; j++)
    {
        
        /*
         * Compute U(J,J) and test for non-positive-definiteness.
         */
        v = ae_v_cdotproduct(&a->ptr.pp_complex[0][j], a->stride, "Conj", &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,j-1));
        ajj = ae_c_sub(a->ptr.pp_complex[j][j],v).x;
        if( ae_fp_less_eq(ajj,0) )
        {
            a->ptr.pp_complex[j][j] = ae_complex_from_d(ajj);
            result = ae_false;
            ae_frame_leave(_state);
            return result;
        }
        ajj = ae_sqrt(ajj, _state);
        a->ptr.pp_complex[j][j] = ae_complex_from_d(ajj);
        
        /*
         * Compute elements J+1:N-1 of row J.
         */
        if( j<n-1 )
        {
            ae_v_cmove(&t2.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "Conj", ae_v_len(0,j-1));
            ae_v_cmove(&t3.ptr.p_complex[j+1], 1, &a->ptr.pp_complex[j][j+1], 1, "N", ae_v_len(j+1,n-1));
            for(i=j+1; i<=n-1; i++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[0][i], a->stride, "N", &t2.ptr.p_complex[0], 1, "N", ae_v_len(0,j-1));
                t3.ptr.p_complex[i] = ae_c_sub(t3.ptr.p_complex[i],v);
            }
            ae_v_cmove(&a->ptr.pp_complex[j][j+1], 1, &t3.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,n-1));
            r = 1/ajj;
            ae_v_cmuld(&a->ptr.pp_complex[j][j+1], 1, ae_v_len(j+1,n-1), r);
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
SVD condition number
*************************************************************************/
static double testmatgenunit_svdcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a1;
    ae_matrix v;
    ae_vector w;
    ae_int_t i;
    ae_int_t j;
    double minw;
    double maxw;
    double result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&a1, n+1, n+1, _state);
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a1.ptr.pp_double[i][j] = a->ptr.pp_double[i-1][j-1];
        }
    }
    if( !testmatgenunit_obsoletesvddecomposition(&a1, n, n, &w, &v, _state) )
    {
        result = 0;
        ae_frame_leave(_state);
        return result;
    }
    minw = w.ptr.p_double[1];
    maxw = w.ptr.p_double[1];
    for(i=2; i<=n; i++)
    {
        if( ae_fp_less(w.ptr.p_double[i],minw) )
        {
            minw = w.ptr.p_double[i];
        }
        if( ae_fp_greater(w.ptr.p_double[i],maxw) )
        {
            maxw = w.ptr.p_double[i];
        }
    }
    result = maxw/minw;
    ae_frame_leave(_state);
    return result;
}


static ae_bool testmatgenunit_obsoletesvddecomposition(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* v,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t nm;
    ae_int_t minmn;
    ae_int_t l;
    ae_int_t k;
    ae_int_t j;
    ae_int_t jj;
    ae_int_t its;
    ae_int_t i;
    double z;
    double y;
    double x;
    double vscale;
    double s;
    double h;
    double g;
    double f;
    double c;
    double anorm;
    ae_vector rv1;
    ae_bool flag;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(w);
    ae_matrix_clear(v);
    ae_vector_init(&rv1, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&rv1, n+1, _state);
    ae_vector_set_length(w, n+1, _state);
    ae_matrix_set_length(v, n+1, n+1, _state);
    result = ae_true;
    if( m<n )
    {
        minmn = m;
    }
    else
    {
        minmn = n;
    }
    g = 0.0;
    vscale = 0.0;
    anorm = 0.0;
    for(i=1; i<=n; i++)
    {
        l = i+1;
        rv1.ptr.p_double[i] = vscale*g;
        g = 0;
        s = 0;
        vscale = 0;
        if( i<=m )
        {
            for(k=i; k<=m; k++)
            {
                vscale = vscale+ae_fabs(a->ptr.pp_double[k][i], _state);
            }
            if( ae_fp_neq(vscale,0.0) )
            {
                for(k=i; k<=m; k++)
                {
                    a->ptr.pp_double[k][i] = a->ptr.pp_double[k][i]/vscale;
                    s = s+a->ptr.pp_double[k][i]*a->ptr.pp_double[k][i];
                }
                f = a->ptr.pp_double[i][i];
                g = -testmatgenunit_extsign(ae_sqrt(s, _state), f, _state);
                h = f*g-s;
                a->ptr.pp_double[i][i] = f-g;
                if( i!=n )
                {
                    for(j=l; j<=n; j++)
                    {
                        s = 0.0;
                        for(k=i; k<=m; k++)
                        {
                            s = s+a->ptr.pp_double[k][i]*a->ptr.pp_double[k][j];
                        }
                        f = s/h;
                        for(k=i; k<=m; k++)
                        {
                            a->ptr.pp_double[k][j] = a->ptr.pp_double[k][j]+f*a->ptr.pp_double[k][i];
                        }
                    }
                }
                for(k=i; k<=m; k++)
                {
                    a->ptr.pp_double[k][i] = vscale*a->ptr.pp_double[k][i];
                }
            }
        }
        w->ptr.p_double[i] = vscale*g;
        g = 0.0;
        s = 0.0;
        vscale = 0.0;
        if( i<=m&&i!=n )
        {
            for(k=l; k<=n; k++)
            {
                vscale = vscale+ae_fabs(a->ptr.pp_double[i][k], _state);
            }
            if( ae_fp_neq(vscale,0.0) )
            {
                for(k=l; k<=n; k++)
                {
                    a->ptr.pp_double[i][k] = a->ptr.pp_double[i][k]/vscale;
                    s = s+a->ptr.pp_double[i][k]*a->ptr.pp_double[i][k];
                }
                f = a->ptr.pp_double[i][l];
                g = -testmatgenunit_extsign(ae_sqrt(s, _state), f, _state);
                h = f*g-s;
                a->ptr.pp_double[i][l] = f-g;
                for(k=l; k<=n; k++)
                {
                    rv1.ptr.p_double[k] = a->ptr.pp_double[i][k]/h;
                }
                if( i!=m )
                {
                    for(j=l; j<=m; j++)
                    {
                        s = 0.0;
                        for(k=l; k<=n; k++)
                        {
                            s = s+a->ptr.pp_double[j][k]*a->ptr.pp_double[i][k];
                        }
                        for(k=l; k<=n; k++)
                        {
                            a->ptr.pp_double[j][k] = a->ptr.pp_double[j][k]+s*rv1.ptr.p_double[k];
                        }
                    }
                }
                for(k=l; k<=n; k++)
                {
                    a->ptr.pp_double[i][k] = vscale*a->ptr.pp_double[i][k];
                }
            }
        }
        anorm = testmatgenunit_mymax(anorm, ae_fabs(w->ptr.p_double[i], _state)+ae_fabs(rv1.ptr.p_double[i], _state), _state);
    }
    for(i=n; i>=1; i--)
    {
        if( i<n )
        {
            if( ae_fp_neq(g,0.0) )
            {
                for(j=l; j<=n; j++)
                {
                    v->ptr.pp_double[j][i] = a->ptr.pp_double[i][j]/a->ptr.pp_double[i][l]/g;
                }
                for(j=l; j<=n; j++)
                {
                    s = 0.0;
                    for(k=l; k<=n; k++)
                    {
                        s = s+a->ptr.pp_double[i][k]*v->ptr.pp_double[k][j];
                    }
                    for(k=l; k<=n; k++)
                    {
                        v->ptr.pp_double[k][j] = v->ptr.pp_double[k][j]+s*v->ptr.pp_double[k][i];
                    }
                }
            }
            for(j=l; j<=n; j++)
            {
                v->ptr.pp_double[i][j] = 0.0;
                v->ptr.pp_double[j][i] = 0.0;
            }
        }
        v->ptr.pp_double[i][i] = 1.0;
        g = rv1.ptr.p_double[i];
        l = i;
    }
    for(i=minmn; i>=1; i--)
    {
        l = i+1;
        g = w->ptr.p_double[i];
        if( i<n )
        {
            for(j=l; j<=n; j++)
            {
                a->ptr.pp_double[i][j] = 0.0;
            }
        }
        if( ae_fp_neq(g,0.0) )
        {
            g = 1.0/g;
            if( i!=n )
            {
                for(j=l; j<=n; j++)
                {
                    s = 0.0;
                    for(k=l; k<=m; k++)
                    {
                        s = s+a->ptr.pp_double[k][i]*a->ptr.pp_double[k][j];
                    }
                    f = s/a->ptr.pp_double[i][i]*g;
                    for(k=i; k<=m; k++)
                    {
                        a->ptr.pp_double[k][j] = a->ptr.pp_double[k][j]+f*a->ptr.pp_double[k][i];
                    }
                }
            }
            for(j=i; j<=m; j++)
            {
                a->ptr.pp_double[j][i] = a->ptr.pp_double[j][i]*g;
            }
        }
        else
        {
            for(j=i; j<=m; j++)
            {
                a->ptr.pp_double[j][i] = 0.0;
            }
        }
        a->ptr.pp_double[i][i] = a->ptr.pp_double[i][i]+1.0;
    }
    for(k=n; k>=1; k--)
    {
        for(its=1; its<=testmatgenunit_maxsvditerations; its++)
        {
            flag = ae_true;
            for(l=k; l>=1; l--)
            {
                nm = l-1;
                if( ae_fp_eq(ae_fabs(rv1.ptr.p_double[l], _state)+anorm,anorm) )
                {
                    flag = ae_false;
                    break;
                }
                if( ae_fp_eq(ae_fabs(w->ptr.p_double[nm], _state)+anorm,anorm) )
                {
                    break;
                }
            }
            if( flag )
            {
                c = 0.0;
                s = 1.0;
                for(i=l; i<=k; i++)
                {
                    f = s*rv1.ptr.p_double[i];
                    if( ae_fp_neq(ae_fabs(f, _state)+anorm,anorm) )
                    {
                        g = w->ptr.p_double[i];
                        h = testmatgenunit_pythag(f, g, _state);
                        w->ptr.p_double[i] = h;
                        h = 1.0/h;
                        c = g*h;
                        s = -f*h;
                        for(j=1; j<=m; j++)
                        {
                            y = a->ptr.pp_double[j][nm];
                            z = a->ptr.pp_double[j][i];
                            a->ptr.pp_double[j][nm] = y*c+z*s;
                            a->ptr.pp_double[j][i] = -y*s+z*c;
                        }
                    }
                }
            }
            z = w->ptr.p_double[k];
            if( l==k )
            {
                if( ae_fp_less(z,0.0) )
                {
                    w->ptr.p_double[k] = -z;
                    for(j=1; j<=n; j++)
                    {
                        v->ptr.pp_double[j][k] = -v->ptr.pp_double[j][k];
                    }
                }
                break;
            }
            if( its==testmatgenunit_maxsvditerations )
            {
                result = ae_false;
                ae_frame_leave(_state);
                return result;
            }
            x = w->ptr.p_double[l];
            nm = k-1;
            y = w->ptr.p_double[nm];
            g = rv1.ptr.p_double[nm];
            h = rv1.ptr.p_double[k];
            f = ((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
            g = testmatgenunit_pythag(f, 1, _state);
            f = ((x-z)*(x+z)+h*(y/(f+testmatgenunit_extsign(g, f, _state))-h))/x;
            c = 1.0;
            s = 1.0;
            for(j=l; j<=nm; j++)
            {
                i = j+1;
                g = rv1.ptr.p_double[i];
                y = w->ptr.p_double[i];
                h = s*g;
                g = c*g;
                z = testmatgenunit_pythag(f, h, _state);
                rv1.ptr.p_double[j] = z;
                c = f/z;
                s = h/z;
                f = x*c+g*s;
                g = -x*s+g*c;
                h = y*s;
                y = y*c;
                for(jj=1; jj<=n; jj++)
                {
                    x = v->ptr.pp_double[jj][j];
                    z = v->ptr.pp_double[jj][i];
                    v->ptr.pp_double[jj][j] = x*c+z*s;
                    v->ptr.pp_double[jj][i] = -x*s+z*c;
                }
                z = testmatgenunit_pythag(f, h, _state);
                w->ptr.p_double[j] = z;
                if( ae_fp_neq(z,0.0) )
                {
                    z = 1.0/z;
                    c = f*z;
                    s = h*z;
                }
                f = c*g+s*y;
                x = -s*g+c*y;
                for(jj=1; jj<=m; jj++)
                {
                    y = a->ptr.pp_double[jj][j];
                    z = a->ptr.pp_double[jj][i];
                    a->ptr.pp_double[jj][j] = y*c+z*s;
                    a->ptr.pp_double[jj][i] = -y*s+z*c;
                }
            }
            rv1.ptr.p_double[l] = 0.0;
            rv1.ptr.p_double[k] = f;
            w->ptr.p_double[k] = x;
        }
    }
    ae_frame_leave(_state);
    return result;
}


static double testmatgenunit_extsign(double a, double b, ae_state *_state)
{
    double result;


    if( ae_fp_greater_eq(b,0) )
    {
        result = ae_fabs(a, _state);
    }
    else
    {
        result = -ae_fabs(a, _state);
    }
    return result;
}


static double testmatgenunit_mymax(double a, double b, ae_state *_state)
{
    double result;


    if( ae_fp_greater(a,b) )
    {
        result = a;
    }
    else
    {
        result = b;
    }
    return result;
}


static double testmatgenunit_pythag(double a, double b, ae_state *_state)
{
    double result;


    if( ae_fp_less(ae_fabs(a, _state),ae_fabs(b, _state)) )
    {
        result = ae_fabs(b, _state)*ae_sqrt(1+ae_sqr(a/b, _state), _state);
    }
    else
    {
        result = ae_fabs(a, _state)*ae_sqrt(1+ae_sqr(b/a, _state), _state);
    }
    return result;
}



static void testtrfacunit_testcluproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state);
static void testtrfacunit_testrluproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state);





ae_bool testtrfac(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ra;
    ae_matrix ral;
    ae_matrix rau;
    ae_matrix ca;
    ae_matrix cal;
    ae_matrix cau;
    ae_int_t m;
    ae_int_t n;
    ae_int_t mx;
    ae_int_t maxmn;
    ae_int_t i;
    ae_int_t j;
    ae_complex vc;
    double vr;
    ae_bool waserrors;
    ae_bool spderr;
    ae_bool hpderr;
    ae_bool rerr;
    ae_bool cerr;
    ae_bool properr;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ral, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rau, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cal, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cau, 0, 0, DT_COMPLEX, _state, ae_true);

    rerr = ae_false;
    spderr = ae_false;
    cerr = ae_false;
    hpderr = ae_false;
    properr = ae_false;
    waserrors = ae_false;
    maxmn = 4*ablasblocksize(&ra, _state)+1;
    threshold = 1000*ae_machineepsilon*maxmn;
    
    /*
     * test LU
     */
    for(mx=1; mx<=maxmn; mx++)
    {
        
        /*
         * Initialize N/M, both are <=MX,
         * at least one of them is exactly equal to MX
         */
        n = 1+ae_randominteger(mx, _state);
        m = 1+ae_randominteger(mx, _state);
        if( ae_fp_greater(ae_randomreal(_state),0.5) )
        {
            n = mx;
        }
        else
        {
            m = mx;
        }
        
        /*
         * First, test on zero matrix
         */
        ae_matrix_set_length(&ra, m, n, _state);
        ae_matrix_set_length(&ca, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = 0;
                ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
            }
        }
        testtrfacunit_testcluproblem(&ca, m, n, threshold, &cerr, &properr, _state);
        testtrfacunit_testrluproblem(&ra, m, n, threshold, &rerr, &properr, _state);
        
        /*
         * Second, random matrix with moderate condition number
         */
        ae_matrix_set_length(&ra, m, n, _state);
        ae_matrix_set_length(&ca, m, n, _state);
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ra.ptr.pp_double[i][j] = 0;
                ca.ptr.pp_complex[i][j] = ae_complex_from_d(0);
            }
        }
        for(i=0; i<=ae_minint(m, n, _state)-1; i++)
        {
            ra.ptr.pp_double[i][i] = 1+10*ae_randomreal(_state);
            ca.ptr.pp_complex[i][i] = ae_complex_from_d(1+10*ae_randomreal(_state));
        }
        cmatrixrndorthogonalfromtheleft(&ca, m, n, _state);
        cmatrixrndorthogonalfromtheright(&ca, m, n, _state);
        rmatrixrndorthogonalfromtheleft(&ra, m, n, _state);
        rmatrixrndorthogonalfromtheright(&ra, m, n, _state);
        testtrfacunit_testcluproblem(&ca, m, n, threshold, &cerr, &properr, _state);
        testtrfacunit_testrluproblem(&ra, m, n, threshold, &rerr, &properr, _state);
    }
    
    /*
     * Test Cholesky
     */
    for(n=1; n<=maxmn; n++)
    {
        
        /*
         * Load CA (HPD matrix with low condition number),
         *      CAL and CAU - its lower and upper triangles
         */
        hpdmatrixrndcond(n, 1+50*ae_randomreal(_state), &ca, _state);
        ae_matrix_set_length(&cal, n, n, _state);
        ae_matrix_set_length(&cau, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                cal.ptr.pp_complex[i][j] = ae_complex_from_d(i);
                cau.ptr.pp_complex[i][j] = ae_complex_from_d(j);
            }
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_cmove(&cal.ptr.pp_complex[i][0], 1, &ca.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,i));
            ae_v_cmove(&cau.ptr.pp_complex[i][i], 1, &ca.ptr.pp_complex[i][i], 1, "N", ae_v_len(i,n-1));
        }
        
        /*
         * Test HPDMatrixCholesky:
         * 1. it must leave upper (lower) part unchanged
         * 2. max(A-L*L^H) must be small
         */
        if( hpdmatrixcholesky(&cal, n, ae_false, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j>i )
                    {
                        hpderr = hpderr||ae_c_neq_d(cal.ptr.pp_complex[i][j],i);
                    }
                    else
                    {
                        vc = ae_v_cdotproduct(&cal.ptr.pp_complex[i][0], 1, "N", &cal.ptr.pp_complex[j][0], 1, "Conj", ae_v_len(0,j));
                        hpderr = hpderr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],vc), _state),threshold);
                    }
                }
            }
        }
        else
        {
            hpderr = ae_true;
        }
        if( hpdmatrixcholesky(&cau, n, ae_true, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j<i )
                    {
                        hpderr = hpderr||ae_c_neq_d(cau.ptr.pp_complex[i][j],j);
                    }
                    else
                    {
                        vc = ae_v_cdotproduct(&cau.ptr.pp_complex[0][i], cau.stride, "Conj", &cau.ptr.pp_complex[0][j], cau.stride, "N", ae_v_len(0,i));
                        hpderr = hpderr||ae_fp_greater(ae_c_abs(ae_c_sub(ca.ptr.pp_complex[i][j],vc), _state),threshold);
                    }
                }
            }
        }
        else
        {
            hpderr = ae_true;
        }
        
        /*
         * Load RA (SPD matrix with low condition number),
         *      RAL and RAU - its lower and upper triangles
         */
        spdmatrixrndcond(n, 1+50*ae_randomreal(_state), &ra, _state);
        ae_matrix_set_length(&ral, n, n, _state);
        ae_matrix_set_length(&rau, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                ral.ptr.pp_double[i][j] = i;
                rau.ptr.pp_double[i][j] = j;
            }
        }
        for(i=0; i<=n-1; i++)
        {
            ae_v_move(&ral.ptr.pp_double[i][0], 1, &ra.ptr.pp_double[i][0], 1, ae_v_len(0,i));
            ae_v_move(&rau.ptr.pp_double[i][i], 1, &ra.ptr.pp_double[i][i], 1, ae_v_len(i,n-1));
        }
        
        /*
         * Test SPDMatrixCholesky:
         * 1. it must leave upper (lower) part unchanged
         * 2. max(A-L*L^H) must be small
         */
        if( spdmatrixcholesky(&ral, n, ae_false, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j>i )
                    {
                        spderr = spderr||ae_fp_neq(ral.ptr.pp_double[i][j],i);
                    }
                    else
                    {
                        vr = ae_v_dotproduct(&ral.ptr.pp_double[i][0], 1, &ral.ptr.pp_double[j][0], 1, ae_v_len(0,j));
                        spderr = spderr||ae_fp_greater(ae_fabs(ra.ptr.pp_double[i][j]-vr, _state),threshold);
                    }
                }
            }
        }
        else
        {
            spderr = ae_true;
        }
        if( spdmatrixcholesky(&rau, n, ae_true, _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    if( j<i )
                    {
                        spderr = spderr||ae_fp_neq(rau.ptr.pp_double[i][j],j);
                    }
                    else
                    {
                        vr = ae_v_dotproduct(&rau.ptr.pp_double[0][i], rau.stride, &rau.ptr.pp_double[0][j], rau.stride, ae_v_len(0,i));
                        spderr = spderr||ae_fp_greater(ae_fabs(ra.ptr.pp_double[i][j]-vr, _state),threshold);
                    }
                }
            }
        }
        else
        {
            spderr = ae_true;
        }
    }
    
    /*
     * report
     */
    waserrors = (((rerr||spderr)||cerr)||hpderr)||properr;
    if( !silent )
    {
        printf("TESTING TRIANGULAR FACTORIZATIONS\n");
        printf("* REAL:                                  ");
        if( rerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPD:                                   ");
        if( spderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX:                               ");
        if( cerr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* HPD:                                   ");
        if( hpderr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* OTHER PROPERTIES:                      ");
        if( properr )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testtrfacunit_testcluproblem(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ca;
    ae_matrix cl;
    ae_matrix cu;
    ae_matrix ca2;
    ae_vector ct;
    ae_int_t i;
    ae_int_t j;
    ae_int_t minmn;
    ae_complex v;
    ae_vector p;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cl, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cu, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ca2, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&ct, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);

    minmn = ae_minint(m, n, _state);
    
    /*
     * PLU test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_cmove(&ca.ptr.pp_complex[i][0], 1, &a->ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
    }
    cmatrixplu(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=m )
        {
            *properr = ae_false;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        cl.ptr.pp_complex[j][j] = ae_complex_from_d(1.0);
        for(i=j+1; i<=m-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        for(j=i; j<=n-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&cl.ptr.pp_complex[i][0], 1, "N", &cu.ptr.pp_complex[0][j], cu.stride, "N", ae_v_len(0,minmn-1));
            ca2.ptr.pp_complex[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, n, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_cmove(&ct.ptr.p_complex[0], 1, &ca2.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
            ae_v_cmove(&ca2.ptr.pp_complex[i][0], 1, &ca2.ptr.pp_complex[p.ptr.p_int[i]][0], 1, "N", ae_v_len(0,n-1));
            ae_v_cmove(&ca2.ptr.pp_complex[p.ptr.p_int[i]][0], 1, &ct.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_c_abs(ae_c_sub(a->ptr.pp_complex[i][j],ca2.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    
    /*
     * LUP test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_cmove(&ca.ptr.pp_complex[i][0], 1, &a->ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
    }
    cmatrixlup(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=n )
        {
            *properr = ae_false;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        for(i=j; i<=m-1; i++)
        {
            cl.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
        }
        cu.ptr.pp_complex[i][i] = ae_complex_from_d(1.0);
        for(j=i+1; j<=n-1; j++)
        {
            cu.ptr.pp_complex[i][j] = ca.ptr.pp_complex[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_cdotproduct(&cl.ptr.pp_complex[i][0], 1, "N", &cu.ptr.pp_complex[0][j], cu.stride, "N", ae_v_len(0,minmn-1));
            ca2.ptr.pp_complex[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, m, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_cmove(&ct.ptr.p_complex[0], 1, &ca2.ptr.pp_complex[0][i], ca2.stride, "N", ae_v_len(0,m-1));
            ae_v_cmove(&ca2.ptr.pp_complex[0][i], ca2.stride, &ca2.ptr.pp_complex[0][p.ptr.p_int[i]], ca2.stride, "N", ae_v_len(0,m-1));
            ae_v_cmove(&ca2.ptr.pp_complex[0][p.ptr.p_int[i]], ca2.stride, &ct.ptr.p_complex[0], 1, "N", ae_v_len(0,m-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_c_abs(ae_c_sub(a->ptr.pp_complex[i][j],ca2.ptr.pp_complex[i][j]), _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


static void testtrfacunit_testrluproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double threshold,
     ae_bool* err,
     ae_bool* properr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix ca;
    ae_matrix cl;
    ae_matrix cu;
    ae_matrix ca2;
    ae_vector ct;
    ae_int_t i;
    ae_int_t j;
    ae_int_t minmn;
    double v;
    ae_vector p;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ca, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ca2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);

    minmn = ae_minint(m, n, _state);
    
    /*
     * PLU test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_move(&ca.ptr.pp_double[i][0], 1, &a->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    }
    rmatrixplu(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=m )
        {
            *properr = ae_false;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_double[i][j] = 0.0;
        }
        cl.ptr.pp_double[j][j] = 1.0;
        for(i=j+1; i<=m-1; i++)
        {
            cl.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_double[i][j] = 0.0;
        }
        for(j=i; j<=n-1; j++)
        {
            cu.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&cl.ptr.pp_double[i][0], 1, &cu.ptr.pp_double[0][j], cu.stride, ae_v_len(0,minmn-1));
            ca2.ptr.pp_double[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, n, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_move(&ct.ptr.p_double[0], 1, &ca2.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            ae_v_move(&ca2.ptr.pp_double[i][0], 1, &ca2.ptr.pp_double[p.ptr.p_int[i]][0], 1, ae_v_len(0,n-1));
            ae_v_move(&ca2.ptr.pp_double[p.ptr.p_int[i]][0], 1, &ct.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j]-ca2.ptr.pp_double[i][j], _state),threshold);
        }
    }
    
    /*
     * LUP test
     */
    ae_matrix_set_length(&ca, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        ae_v_move(&ca.ptr.pp_double[i][0], 1, &a->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
    }
    rmatrixlup(&ca, m, n, &p, _state);
    for(i=0; i<=minmn-1; i++)
    {
        if( p.ptr.p_int[i]<i||p.ptr.p_int[i]>=n )
        {
            *properr = ae_false;
            ae_frame_leave(_state);
            return;
        }
    }
    ae_matrix_set_length(&cl, m, minmn, _state);
    for(j=0; j<=minmn-1; j++)
    {
        for(i=0; i<=j-1; i++)
        {
            cl.ptr.pp_double[i][j] = 0.0;
        }
        for(i=j; i<=m-1; i++)
        {
            cl.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&cu, minmn, n, _state);
    for(i=0; i<=minmn-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            cu.ptr.pp_double[i][j] = 0.0;
        }
        cu.ptr.pp_double[i][i] = 1.0;
        for(j=i+1; j<=n-1; j++)
        {
            cu.ptr.pp_double[i][j] = ca.ptr.pp_double[i][j];
        }
    }
    ae_matrix_set_length(&ca2, m, n, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&cl.ptr.pp_double[i][0], 1, &cu.ptr.pp_double[0][j], cu.stride, ae_v_len(0,minmn-1));
            ca2.ptr.pp_double[i][j] = v;
        }
    }
    ae_vector_set_length(&ct, m, _state);
    for(i=minmn-1; i>=0; i--)
    {
        if( i!=p.ptr.p_int[i] )
        {
            ae_v_move(&ct.ptr.p_double[0], 1, &ca2.ptr.pp_double[0][i], ca2.stride, ae_v_len(0,m-1));
            ae_v_move(&ca2.ptr.pp_double[0][i], ca2.stride, &ca2.ptr.pp_double[0][p.ptr.p_int[i]], ca2.stride, ae_v_len(0,m-1));
            ae_v_move(&ca2.ptr.pp_double[0][p.ptr.p_int[i]], ca2.stride, &ct.ptr.p_double[0], 1, ae_v_len(0,m-1));
        }
    }
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            *err = *err||ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j]-ca2.ptr.pp_double[i][j], _state),threshold);
        }
    }
    ae_frame_leave(_state);
}



static void testtrlinsolveunit_makeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);





/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testtrlinsolve(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxmn;
    ae_int_t passcount;
    double threshold;
    ae_matrix aeffective;
    ae_matrix aparam;
    ae_vector xe;
    ae_vector b;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t cnts;
    ae_int_t cntu;
    ae_int_t cntt;
    ae_int_t cntm;
    ae_bool waserrors;
    ae_bool isupper;
    ae_bool istrans;
    ae_bool isunit;
    double v;
    double s;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&aeffective, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&aparam, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    maxmn = 15;
    passcount = 15;
    threshold = 1000*ae_machineepsilon;
    
    /*
     * Different problems
     */
    for(n=1; n<=maxmn; n++)
    {
        ae_matrix_set_length(&aeffective, n-1+1, n-1+1, _state);
        ae_matrix_set_length(&aparam, n-1+1, n-1+1, _state);
        ae_vector_set_length(&xe, n-1+1, _state);
        ae_vector_set_length(&b, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            for(cnts=0; cnts<=1; cnts++)
            {
                for(cntu=0; cntu<=1; cntu++)
                {
                    for(cntt=0; cntt<=1; cntt++)
                    {
                        for(cntm=0; cntm<=2; cntm++)
                        {
                            isupper = cnts==0;
                            isunit = cntu==0;
                            istrans = cntt==0;
                            
                            /*
                             * Skip meaningless combinations of parameters:
                             * (matrix is singular) AND (matrix is unit diagonal)
                             */
                            if( cntm==2&&isunit )
                            {
                                continue;
                            }
                            
                            /*
                             * Clear matrices
                             */
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    aeffective.ptr.pp_double[i][j] = 0;
                                    aparam.ptr.pp_double[i][j] = 0;
                                }
                            }
                            
                            /*
                             * Prepare matrices
                             */
                            if( isupper )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=i; j<=n-1; j++)
                                    {
                                        aeffective.ptr.pp_double[i][j] = 0.9*(2*ae_randomreal(_state)-1);
                                        aparam.ptr.pp_double[i][j] = aeffective.ptr.pp_double[i][j];
                                    }
                                    aeffective.ptr.pp_double[i][i] = (2*ae_randominteger(2, _state)-1)*(0.8+ae_randomreal(_state));
                                    aparam.ptr.pp_double[i][i] = aeffective.ptr.pp_double[i][i];
                                }
                            }
                            else
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    for(j=0; j<=i; j++)
                                    {
                                        aeffective.ptr.pp_double[i][j] = 0.9*(2*ae_randomreal(_state)-1);
                                        aparam.ptr.pp_double[i][j] = aeffective.ptr.pp_double[i][j];
                                    }
                                    aeffective.ptr.pp_double[i][i] = (2*ae_randominteger(2, _state)-1)*(0.8+ae_randomreal(_state));
                                    aparam.ptr.pp_double[i][i] = aeffective.ptr.pp_double[i][i];
                                }
                            }
                            if( isunit )
                            {
                                for(i=0; i<=n-1; i++)
                                {
                                    aeffective.ptr.pp_double[i][i] = 1;
                                    aparam.ptr.pp_double[i][i] = 0;
                                }
                            }
                            if( istrans )
                            {
                                if( isupper )
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=i+1; j<=n-1; j++)
                                        {
                                            aeffective.ptr.pp_double[j][i] = aeffective.ptr.pp_double[i][j];
                                            aeffective.ptr.pp_double[i][j] = 0;
                                        }
                                    }
                                }
                                else
                                {
                                    for(i=0; i<=n-1; i++)
                                    {
                                        for(j=i+1; j<=n-1; j++)
                                        {
                                            aeffective.ptr.pp_double[i][j] = aeffective.ptr.pp_double[j][i];
                                            aeffective.ptr.pp_double[j][i] = 0;
                                        }
                                    }
                                }
                            }
                            
                            /*
                             * Prepare task, solve, compare
                             */
                            for(i=0; i<=n-1; i++)
                            {
                                xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                v = ae_v_dotproduct(&aeffective.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
                                b.ptr.p_double[i] = v;
                            }
                            rmatrixtrsafesolve(&aparam, n, &b, &s, isupper, istrans, isunit, _state);
                            ae_v_muld(&xe.ptr.p_double[0], 1, ae_v_len(0,n-1), s);
                            ae_v_sub(&xe.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            v = ae_v_dotproduct(&xe.ptr.p_double[0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            v = ae_sqrt(v, _state);
                            waserrors = waserrors||ae_fp_greater(v,threshold);
                        }
                    }
                }
            }
        }
    }
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING RMatrixTRSafeSolve\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testtrlinsolveunit_makeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}



static void testsafesolveunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testsafesolveunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);





/*************************************************************************
Main unittest subroutine
*************************************************************************/
ae_bool testsafesolve(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxmn;
    double threshold;
    ae_bool rerrors;
    ae_bool cerrors;
    ae_bool waserrors;
    ae_bool isupper;
    ae_int_t trans;
    ae_bool isunit;
    double scalea;
    double growth;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t j1;
    ae_int_t j2;
    ae_complex cv;
    ae_matrix ca;
    ae_matrix cea;
    ae_matrix ctmpa;
    ae_vector cxs;
    ae_vector cxe;
    double rv;
    ae_matrix ra;
    ae_matrix rea;
    ae_matrix rtmpa;
    ae_vector rxs;
    ae_vector rxe;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&ca, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cea, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ctmpa, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cxs, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cxe, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rea, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&rtmpa, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rxs, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rxe, 0, DT_REAL, _state, ae_true);

    maxmn = 30;
    threshold = 100000*ae_machineepsilon;
    rerrors = ae_false;
    cerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Different problems: general tests
     */
    for(n=1; n<=maxmn; n++)
    {
        
        /*
         * test complex solver with well-conditioned matrix:
         * 1. generate A: fill off-diagonal elements with small values,
         *    diagonal elements are filled with larger values
         * 2. generate 'effective' A
         * 3. prepare task (exact X is stored in CXE, right part - in CXS),
         *    solve and compare CXS and CXE
         */
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        trans = ae_randominteger(3, _state);
        isunit = ae_fp_greater(ae_randomreal(_state),0.5);
        scalea = ae_randomreal(_state)+0.5;
        ae_matrix_set_length(&ca, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    ca.ptr.pp_complex[i][j].x = (2*ae_randominteger(2, _state)-1)*(5+ae_randomreal(_state));
                    ca.ptr.pp_complex[i][j].y = (2*ae_randominteger(2, _state)-1)*(5+ae_randomreal(_state));
                }
                else
                {
                    ca.ptr.pp_complex[i][j].x = 0.2*ae_randomreal(_state)-0.1;
                    ca.ptr.pp_complex[i][j].y = 0.2*ae_randomreal(_state)-0.1;
                }
            }
        }
        testsafesolveunit_cmatrixmakeacopy(&ca, n, n, &ctmpa, _state);
        for(i=0; i<=n-1; i++)
        {
            if( isupper )
            {
                j1 = 0;
                j2 = i-1;
            }
            else
            {
                j1 = i+1;
                j2 = n-1;
            }
            for(j=j1; j<=j2; j++)
            {
                ctmpa.ptr.pp_complex[i][j] = ae_complex_from_d(0);
            }
            if( isunit )
            {
                ctmpa.ptr.pp_complex[i][i] = ae_complex_from_d(1);
            }
        }
        ae_matrix_set_length(&cea, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            if( trans==0 )
            {
                ae_v_cmoved(&cea.ptr.pp_complex[i][0], 1, &ctmpa.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1), scalea);
            }
            if( trans==1 )
            {
                ae_v_cmoved(&cea.ptr.pp_complex[0][i], cea.stride, &ctmpa.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1), scalea);
            }
            if( trans==2 )
            {
                ae_v_cmoved(&cea.ptr.pp_complex[0][i], cea.stride, &ctmpa.ptr.pp_complex[i][0], 1, "Conj", ae_v_len(0,n-1), scalea);
            }
        }
        ae_vector_set_length(&cxe, n, _state);
        for(i=0; i<=n-1; i++)
        {
            cxe.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            cxe.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&cxs, n, _state);
        for(i=0; i<=n-1; i++)
        {
            cv = ae_v_cdotproduct(&cea.ptr.pp_complex[i][0], 1, "N", &cxe.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
            cxs.ptr.p_complex[i] = cv;
        }
        if( cmatrixscaledtrsafesolve(&ca, scalea, n, &cxs, isupper, trans, isunit, ae_sqrt(ae_maxrealnumber, _state), _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                cerrors = cerrors||ae_fp_greater(ae_c_abs(ae_c_sub(cxs.ptr.p_complex[i],cxe.ptr.p_complex[i]), _state),threshold);
            }
        }
        else
        {
            cerrors = ae_true;
        }
        
        /*
         * same with real
         */
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        trans = ae_randominteger(2, _state);
        isunit = ae_fp_greater(ae_randomreal(_state),0.5);
        scalea = ae_randomreal(_state)+0.5;
        ae_matrix_set_length(&ra, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    ra.ptr.pp_double[i][j] = (2*ae_randominteger(2, _state)-1)*(5+ae_randomreal(_state));
                }
                else
                {
                    ra.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
                }
            }
        }
        testsafesolveunit_rmatrixmakeacopy(&ra, n, n, &rtmpa, _state);
        for(i=0; i<=n-1; i++)
        {
            if( isupper )
            {
                j1 = 0;
                j2 = i-1;
            }
            else
            {
                j1 = i+1;
                j2 = n-1;
            }
            for(j=j1; j<=j2; j++)
            {
                rtmpa.ptr.pp_double[i][j] = 0;
            }
            if( isunit )
            {
                rtmpa.ptr.pp_double[i][i] = 1;
            }
        }
        ae_matrix_set_length(&rea, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            if( trans==0 )
            {
                ae_v_moved(&rea.ptr.pp_double[i][0], 1, &rtmpa.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), scalea);
            }
            if( trans==1 )
            {
                ae_v_moved(&rea.ptr.pp_double[0][i], rea.stride, &rtmpa.ptr.pp_double[i][0], 1, ae_v_len(0,n-1), scalea);
            }
        }
        ae_vector_set_length(&rxe, n, _state);
        for(i=0; i<=n-1; i++)
        {
            rxe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&rxs, n, _state);
        for(i=0; i<=n-1; i++)
        {
            rv = ae_v_dotproduct(&rea.ptr.pp_double[i][0], 1, &rxe.ptr.p_double[0], 1, ae_v_len(0,n-1));
            rxs.ptr.p_double[i] = rv;
        }
        if( rmatrixscaledtrsafesolve(&ra, scalea, n, &rxs, isupper, trans, isunit, ae_sqrt(ae_maxrealnumber, _state), _state) )
        {
            for(i=0; i<=n-1; i++)
            {
                rerrors = rerrors||ae_fp_greater(ae_fabs(rxs.ptr.p_double[i]-rxe.ptr.p_double[i], _state),threshold);
            }
        }
        else
        {
            rerrors = ae_true;
        }
    }
    
    /*
     * Special test with diagonal ill-conditioned matrix:
     * * ability to solve it when resulting growth is less than threshold
     * * ability to stop solve when resulting growth is greater than threshold
     *
     * A = diag(1, 1/growth)
     * b = (1, 0.5)
     */
    n = 2;
    growth = 10;
    ae_matrix_set_length(&ca, n, n, _state);
    ca.ptr.pp_complex[0][0] = ae_complex_from_d(1);
    ca.ptr.pp_complex[0][1] = ae_complex_from_d(0);
    ca.ptr.pp_complex[1][0] = ae_complex_from_d(0);
    ca.ptr.pp_complex[1][1] = ae_complex_from_d(1/growth);
    ae_vector_set_length(&cxs, n, _state);
    cxs.ptr.p_complex[0] = ae_complex_from_d(1.0);
    cxs.ptr.p_complex[1] = ae_complex_from_d(0.5);
    cerrors = cerrors||!cmatrixscaledtrsafesolve(&ca, 1.0, n, &cxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), ae_false, 1.05*ae_maxreal(ae_c_abs(cxs.ptr.p_complex[1], _state)*growth, 1.0, _state), _state);
    cerrors = cerrors||!cmatrixscaledtrsafesolve(&ca, 1.0, n, &cxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), ae_false, 0.95*ae_maxreal(ae_c_abs(cxs.ptr.p_complex[1], _state)*growth, 1.0, _state), _state);
    ae_matrix_set_length(&ra, n, n, _state);
    ra.ptr.pp_double[0][0] = 1;
    ra.ptr.pp_double[0][1] = 0;
    ra.ptr.pp_double[1][0] = 0;
    ra.ptr.pp_double[1][1] = 1/growth;
    ae_vector_set_length(&rxs, n, _state);
    rxs.ptr.p_double[0] = 1.0;
    rxs.ptr.p_double[1] = 0.5;
    rerrors = rerrors||!rmatrixscaledtrsafesolve(&ra, 1.0, n, &rxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), ae_false, 1.05*ae_maxreal(ae_fabs(rxs.ptr.p_double[1], _state)*growth, 1.0, _state), _state);
    rerrors = rerrors||!rmatrixscaledtrsafesolve(&ra, 1.0, n, &rxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), ae_false, 0.95*ae_maxreal(ae_fabs(rxs.ptr.p_double[1], _state)*growth, 1.0, _state), _state);
    
    /*
     * Special test with diagonal degenerate matrix:
     * * ability to solve it when resulting growth is less than threshold
     * * ability to stop solve when resulting growth is greater than threshold
     *
     * A = diag(1, 0)
     * b = (1, 0.5)
     */
    n = 2;
    ae_matrix_set_length(&ca, n, n, _state);
    ca.ptr.pp_complex[0][0] = ae_complex_from_d(1);
    ca.ptr.pp_complex[0][1] = ae_complex_from_d(0);
    ca.ptr.pp_complex[1][0] = ae_complex_from_d(0);
    ca.ptr.pp_complex[1][1] = ae_complex_from_d(0);
    ae_vector_set_length(&cxs, n, _state);
    cxs.ptr.p_complex[0] = ae_complex_from_d(1.0);
    cxs.ptr.p_complex[1] = ae_complex_from_d(0.5);
    cerrors = cerrors||cmatrixscaledtrsafesolve(&ca, 1.0, n, &cxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(3, _state), ae_false, ae_sqrt(ae_maxrealnumber, _state), _state);
    ae_matrix_set_length(&ra, n, n, _state);
    ra.ptr.pp_double[0][0] = 1;
    ra.ptr.pp_double[0][1] = 0;
    ra.ptr.pp_double[1][0] = 0;
    ra.ptr.pp_double[1][1] = 0;
    ae_vector_set_length(&rxs, n, _state);
    rxs.ptr.p_double[0] = 1.0;
    rxs.ptr.p_double[1] = 0.5;
    rerrors = rerrors||rmatrixscaledtrsafesolve(&ra, 1.0, n, &rxs, ae_fp_greater(ae_randomreal(_state),0.5), ae_randominteger(2, _state), ae_false, ae_sqrt(ae_maxrealnumber, _state), _state);
    
    /*
     * report
     */
    waserrors = rerrors||cerrors;
    if( !silent )
    {
        printf("TESTING SAFE TR SOLVER\n");
        printf("REAL:                                    ");
        if( !rerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX:                                 ");
        if( !cerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testsafesolveunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testsafesolveunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}



static double testrcondunit_threshold50 = 0.25;
static double testrcondunit_threshold90 = 0.10;
static void testrcondunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testrcondunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testrcondunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testrcondunit_rmatrixgenzero(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_rmatrixinvmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testrcondunit_rmatrixinvmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_rmatrixinvmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static void testrcondunit_rmatrixrefrcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state);
static void testrcondunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static void testrcondunit_cmatrixgenzero(/* Complex */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_cmatrixinvmattr(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testrcondunit_cmatrixinvmatlu(/* Complex */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state);
static ae_bool testrcondunit_cmatrixinvmat(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static void testrcondunit_cmatrixrefrcond(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state);
static ae_bool testrcondunit_testrmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testcmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testrmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testspdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testcmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);
static ae_bool testrcondunit_testhpdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state);





ae_bool testrcond(ae_bool silent, ae_state *_state)
{
    ae_int_t maxn;
    ae_int_t passcount;
    ae_bool waserrors;
    ae_bool rtrerr;
    ae_bool ctrerr;
    ae_bool rerr;
    ae_bool cerr;
    ae_bool spderr;
    ae_bool hpderr;
    ae_bool result;


    maxn = 10;
    passcount = 100;
    
    /*
     * report
     */
    rtrerr = !testrcondunit_testrmatrixtrrcond(maxn, passcount, _state);
    ctrerr = !testrcondunit_testcmatrixtrrcond(maxn, passcount, _state);
    rerr = !testrcondunit_testrmatrixrcond(maxn, passcount, _state);
    cerr = !testrcondunit_testcmatrixrcond(maxn, passcount, _state);
    spderr = !testrcondunit_testspdmatrixrcond(maxn, passcount, _state);
    hpderr = !testrcondunit_testhpdmatrixrcond(maxn, passcount, _state);
    waserrors = ((((rtrerr||ctrerr)||rerr)||cerr)||spderr)||hpderr;
    if( !silent )
    {
        printf("TESTING RCOND\n");
        printf("REAL TRIANGULAR:                         ");
        if( !rtrerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX TRIANGULAR:                      ");
        if( !ctrerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("REAL:                                    ");
        if( !rerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("SPD:                                     ");
        if( !spderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HPD:                                     ");
        if( !hpderr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("COMPLEX:                                 ");
        if( !cerr )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testrcondunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testrcondunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_double[i][j] = 1+2*i+3*j;
            }
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testrcondunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_d(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Generate matrix with given condition number C (2-norm)
*************************************************************************/
static void testrcondunit_rmatrixgenzero(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_matrix_set_length(a0, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a0->ptr.pp_double[i][j] = 0;
        }
    }
}


/*************************************************************************
triangular inverse
*************************************************************************/
static ae_bool testrcondunit_rmatrixinvmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    double v;
    double ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = -1;
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_move(&t.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][i+1], 1, &t.ptr.p_double[i+1], 1, ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = 0;
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = -1;
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_move(&t.ptr.p_double[j+1], 1, &a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &t.ptr.p_double[j+1], 1, ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = 0;
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LU inverse
*************************************************************************/
static ae_bool testrcondunit_rmatrixinvmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    
    /*
     * Quick return if possible
     */
    if( n==0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_vector_set_length(&work, n-1+1, _state);
    
    /*
     * Form inv(U)
     */
    if( !testrcondunit_rmatrixinvmattr(a, n, ae_true, ae_false, _state) )
    {
        result = ae_false;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Solve the equation inv(A)*L = inv(U) for inv(A).
     */
    for(j=n-1; j>=0; j--)
    {
        
        /*
         * Copy current column of L to WORK and replace with zeros.
         */
        for(i=j+1; i<=n-1; i++)
        {
            work.ptr.p_double[i] = a->ptr.pp_double[i][j];
            a->ptr.pp_double[i][j] = 0;
        }
        
        /*
         * Compute current column of inv(A).
         */
        if( j<n-1 )
        {
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &work.ptr.p_double[j+1], 1, ae_v_len(j+1,n-1));
                a->ptr.pp_double[i][j] = a->ptr.pp_double[i][j]-v;
            }
        }
    }
    
    /*
     * Apply column interchanges.
     */
    for(j=n-2; j>=0; j--)
    {
        jp = pivots->ptr.p_int[j];
        if( jp!=j )
        {
            ae_v_move(&work.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][j], a->stride, &a->ptr.pp_double[0][jp], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][jp], a->stride, &work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Matrix inverse
*************************************************************************/
static ae_bool testrcondunit_rmatrixinvmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    rmatrixlu(a, n, n, &pivots, _state);
    result = testrcondunit_rmatrixinvmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
reference RCond
*************************************************************************/
static void testrcondunit_rmatrixrefrcond(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix inva;
    double nrm1a;
    double nrminfa;
    double nrm1inva;
    double nrminfinva;
    double v;
    ae_int_t k;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    *rc1 = 0;
    *rcinf = 0;
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * inv A
     */
    testrcondunit_rmatrixmakeacopy(a, n, n, &inva, _state);
    if( !testrcondunit_rmatrixinvmat(&inva, n, _state) )
    {
        *rc1 = 0;
        *rcinf = 0;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * norm A
     */
    nrm1a = 0;
    nrminfa = 0;
    for(k=0; k<=n-1; k++)
    {
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(a->ptr.pp_double[i][k], _state);
        }
        nrm1a = ae_maxreal(nrm1a, v, _state);
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(a->ptr.pp_double[k][i], _state);
        }
        nrminfa = ae_maxreal(nrminfa, v, _state);
    }
    
    /*
     * norm inv A
     */
    nrm1inva = 0;
    nrminfinva = 0;
    for(k=0; k<=n-1; k++)
    {
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(inva.ptr.pp_double[i][k], _state);
        }
        nrm1inva = ae_maxreal(nrm1inva, v, _state);
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_fabs(inva.ptr.pp_double[k][i], _state);
        }
        nrminfinva = ae_maxreal(nrminfinva, v, _state);
    }
    
    /*
     * result
     */
    *rc1 = nrm1inva*nrm1a;
    *rcinf = nrminfinva*nrminfa;
    ae_frame_leave(_state);
}


/*************************************************************************
Copy
*************************************************************************/
static void testrcondunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Generate matrix with given condition number C (2-norm)
*************************************************************************/
static void testrcondunit_cmatrixgenzero(/* Complex */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_matrix_set_length(a0, n, n, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a0->ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
    }
}


/*************************************************************************
triangular inverse
*************************************************************************/
static ae_bool testrcondunit_cmatrixinvmattr(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_complex ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&t, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_d(-1);
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_cmove(&t.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][i+1], 1, "N", &t.ptr.p_complex[i+1], 1, "N", ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = ae_complex_from_d(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_c_eq_d(a->ptr.pp_complex[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_complex[j][j] = ae_c_d_div(1,a->ptr.pp_complex[j][j]);
                ajj = ae_c_neg(a->ptr.pp_complex[j][j]);
            }
            else
            {
                ajj = ae_complex_from_d(-1);
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_cmove(&t.ptr.p_complex[j+1], 1, &a->ptr.pp_complex[j+1][j], a->stride, "N", ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_cdotproduct(&a->ptr.pp_complex[i][j+1], 1, "N", &t.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = ae_complex_from_d(0);
                    }
                    if( nounit )
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,ae_c_mul(a->ptr.pp_complex[i][i],t.ptr.p_complex[i]));
                    }
                    else
                    {
                        a->ptr.pp_complex[i][j] = ae_c_add(v,t.ptr.p_complex[i]);
                    }
                }
                ae_v_cmulc(&a->ptr.pp_complex[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LU inverse
*************************************************************************/
static ae_bool testrcondunit_cmatrixinvmatlu(/* Complex */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    ae_complex v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&work, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    
    /*
     * Quick return if possible
     */
    if( n==0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_vector_set_length(&work, n-1+1, _state);
    
    /*
     * Form inv(U)
     */
    if( !testrcondunit_cmatrixinvmattr(a, n, ae_true, ae_false, _state) )
    {
        result = ae_false;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Solve the equation inv(A)*L = inv(U) for inv(A).
     */
    for(j=n-1; j>=0; j--)
    {
        
        /*
         * Copy current column of L to WORK and replace with zeros.
         */
        for(i=j+1; i<=n-1; i++)
        {
            work.ptr.p_complex[i] = a->ptr.pp_complex[i][j];
            a->ptr.pp_complex[i][j] = ae_complex_from_d(0);
        }
        
        /*
         * Compute current column of inv(A).
         */
        if( j<n-1 )
        {
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[i][j+1], 1, "N", &work.ptr.p_complex[j+1], 1, "N", ae_v_len(j+1,n-1));
                a->ptr.pp_complex[i][j] = ae_c_sub(a->ptr.pp_complex[i][j],v);
            }
        }
    }
    
    /*
     * Apply column interchanges.
     */
    for(j=n-2; j>=0; j--)
    {
        jp = pivots->ptr.p_int[j];
        if( jp!=j )
        {
            ae_v_cmove(&work.ptr.p_complex[0], 1, &a->ptr.pp_complex[0][j], a->stride, "N", ae_v_len(0,n-1));
            ae_v_cmove(&a->ptr.pp_complex[0][j], a->stride, &a->ptr.pp_complex[0][jp], a->stride, "N", ae_v_len(0,n-1));
            ae_v_cmove(&a->ptr.pp_complex[0][jp], a->stride, &work.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Matrix inverse
*************************************************************************/
static ae_bool testrcondunit_cmatrixinvmat(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    cmatrixlu(a, n, n, &pivots, _state);
    result = testrcondunit_cmatrixinvmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
reference RCond
*************************************************************************/
static void testrcondunit_cmatrixrefrcond(/* Complex */ ae_matrix* a,
     ae_int_t n,
     double* rc1,
     double* rcinf,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix inva;
    double nrm1a;
    double nrminfa;
    double nrm1inva;
    double nrminfinva;
    double v;
    ae_int_t k;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    *rc1 = 0;
    *rcinf = 0;
    ae_matrix_init(&inva, 0, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * inv A
     */
    testrcondunit_cmatrixmakeacopy(a, n, n, &inva, _state);
    if( !testrcondunit_cmatrixinvmat(&inva, n, _state) )
    {
        *rc1 = 0;
        *rcinf = 0;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * norm A
     */
    nrm1a = 0;
    nrminfa = 0;
    for(k=0; k<=n-1; k++)
    {
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(a->ptr.pp_complex[i][k], _state);
        }
        nrm1a = ae_maxreal(nrm1a, v, _state);
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(a->ptr.pp_complex[k][i], _state);
        }
        nrminfa = ae_maxreal(nrminfa, v, _state);
    }
    
    /*
     * norm inv A
     */
    nrm1inva = 0;
    nrminfinva = 0;
    for(k=0; k<=n-1; k++)
    {
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(inva.ptr.pp_complex[i][k], _state);
        }
        nrm1inva = ae_maxreal(nrm1inva, v, _state);
        v = 0;
        for(i=0; i<=n-1; i++)
        {
            v = v+ae_c_abs(inva.ptr.pp_complex[k][i], _state);
        }
        nrminfinva = ae_maxreal(nrminfinva, v, _state);
    }
    
    /*
     * result
     */
    *rc1 = nrm1inva*nrm1a;
    *rcinf = nrminfinva*nrminfa;
    ae_frame_leave(_state);
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testrmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ea;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j1;
    ae_int_t j2;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool isupper;
    ae_bool isunit;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ea, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_rmatrixgenzero(&a, n, _state);
        errspec = errspec||ae_fp_neq(rmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        errspec = errspec||ae_fp_neq(rmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = 0;
            q90.ptr.p_double[i] = 0;
        }
        for(pass=1; pass<=passcount; pass++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            isunit = ae_fp_greater(ae_randomreal(_state),0.5);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = ae_randomreal(_state)-0.5;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 1+ae_randomreal(_state);
            }
            testrcondunit_rmatrixmakeacopy(&a, n, n, &ea, _state);
            for(i=0; i<=n-1; i++)
            {
                if( isupper )
                {
                    j1 = 0;
                    j2 = i-1;
                }
                else
                {
                    j1 = i+1;
                    j2 = n-1;
                }
                for(j=j1; j<=j2; j++)
                {
                    ea.ptr.pp_double[i][j] = 0;
                }
                if( isunit )
                {
                    ea.ptr.pp_double[i][i] = 1;
                }
            }
            testrcondunit_rmatrixrefrcond(&ea, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm
             */
            v = 1/rmatrixtrrcond1(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm
             */
            v = 1/rmatrixtrrcondinf(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            a.ptr.pp_double[0][0] = 1;
            a.ptr.pp_double[n-1][n-1] = 1;
            errspec = errspec||ae_fp_neq(rmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 1;
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_double[i][i] = 0.1*ae_maxrealnumber;
            errspec = errspec||ae_fp_neq(rmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testcmatrixtrrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix ea;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t j1;
    ae_int_t j2;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool isupper;
    ae_bool isunit;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&ea, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_cmatrixgenzero(&a, n, _state);
        errspec = errspec||ae_fp_neq(cmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        errspec = errspec||ae_fp_neq(cmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = 0;
            q90.ptr.p_double[i] = 0;
        }
        for(pass=1; pass<=passcount; pass++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            isunit = ae_fp_greater(ae_randomreal(_state),0.5);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j].x = ae_randomreal(_state)-0.5;
                    a.ptr.pp_complex[i][j].y = ae_randomreal(_state)-0.5;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i].x = 1+ae_randomreal(_state);
                a.ptr.pp_complex[i][i].y = 1+ae_randomreal(_state);
            }
            testrcondunit_cmatrixmakeacopy(&a, n, n, &ea, _state);
            for(i=0; i<=n-1; i++)
            {
                if( isupper )
                {
                    j1 = 0;
                    j2 = i-1;
                }
                else
                {
                    j1 = i+1;
                    j2 = n-1;
                }
                for(j=j1; j<=j2; j++)
                {
                    ea.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                }
                if( isunit )
                {
                    ea.ptr.pp_complex[i][i] = ae_complex_from_d(1);
                }
            }
            testrcondunit_cmatrixrefrcond(&ea, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm
             */
            v = 1/cmatrixtrrcond1(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm
             */
            v = 1/cmatrixtrrcondinf(&a, n, isupper, isunit, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            a.ptr.pp_complex[0][0] = ae_complex_from_d(1);
            a.ptr.pp_complex[n-1][n-1] = ae_complex_from_d(1);
            errspec = errspec||ae_fp_neq(cmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i] = ae_complex_from_d(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*ae_maxrealnumber);
            errspec = errspec||ae_fp_neq(cmatrixtrrcond1(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixtrrcondinf(&a, n, ae_fp_greater(ae_randomreal(_state),0.5), ae_false, _state),0);
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testrmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 3+1, _state);
    ae_vector_set_length(&q90, 3+1, _state);
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_rmatrixgenzero(&a, n, _state);
        testrcondunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
        rmatrixlu(&lua, n, n, &p, _state);
        errspec = errspec||ae_fp_neq(rmatrixrcond1(&a, n, _state),0);
        errspec = errspec||ae_fp_neq(rmatrixrcondinf(&a, n, _state),0);
        errspec = errspec||ae_fp_neq(rmatrixlurcond1(&lua, n, _state),0);
        errspec = errspec||ae_fp_neq(rmatrixlurcondinf(&lua, n, _state),0);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        for(i=0; i<=3; i++)
        {
            q50.ptr.p_double[i] = 0;
            q90.ptr.p_double[i] = 0;
        }
        for(pass=1; pass<=passcount; pass++)
        {
            rmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log(1000, _state), _state), &a, _state);
            testrcondunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
            rmatrixlu(&lua, n, n, &p, _state);
            testrcondunit_rmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm, normal
             */
            v = 1/rmatrixrcond1(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * 1-norm, LU
             */
            v = 1/rmatrixlurcond1(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm, normal
             */
            v = 1/rmatrixrcondinf(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[2] = q50.ptr.p_double[2]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[2] = q90.ptr.p_double[2]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
            
            /*
             * Inf-norm, LU
             */
            v = 1/rmatrixlurcondinf(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[3] = q50.ptr.p_double[3]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[3] = q90.ptr.p_double[3]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=3; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            a.ptr.pp_double[0][0] = 1;
            a.ptr.pp_double[n-1][n-1] = 1;
            errspec = errspec||ae_fp_neq(rmatrixrcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixrcondinf(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixlurcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixlurcondinf(&a, n, _state),0);
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 1;
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_double[i][i] = 0.1*ae_maxrealnumber;
            errspec = errspec||ae_fp_neq(rmatrixrcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixrcondinf(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixlurcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(rmatrixlurcondinf(&a, n, _state),0);
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testspdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    ae_bool isupper;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = 0;
            q90.ptr.p_double[i] = 0;
        }
        for(pass=1; pass<=passcount; pass++)
        {
            spdmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log(1000, _state), _state), &a, _state);
            testrcondunit_rmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            testrcondunit_rmatrixdrophalf(&a, n, isupper, _state);
            testrcondunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
            spdmatrixcholesky(&cha, n, isupper, _state);
            
            /*
             * normal
             */
            v = 1/spdmatrixrcond(&a, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Cholesky
             */
            v = 1/spdmatrixcholeskyrcond(&cha, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            a.ptr.pp_double[0][0] = 1;
            a.ptr.pp_double[n-1][n-1] = 1;
            errspec = errspec||ae_fp_neq(spdmatrixrcond(&a, n, isupper, _state),-1);
            errspec = errspec||ae_fp_neq(spdmatrixcholeskyrcond(&a, n, isupper, _state),0);
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 0.0;
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_double[i][i] = 1;
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_double[i][i] = 0.1*ae_maxrealnumber;
            errspec = errspec||ae_fp_neq(spdmatrixrcond(&a, n, isupper, _state),0);
            errspec = errspec||ae_fp_neq(spdmatrixcholeskyrcond(&a, n, isupper, _state),0);
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testcmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errless;
    ae_bool errspec;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&q50, 3+1, _state);
    ae_vector_set_length(&q90, 3+1, _state);
    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    
    /*
     * process
     */
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * special test for zero matrix
         */
        testrcondunit_cmatrixgenzero(&a, n, _state);
        testrcondunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
        cmatrixlu(&lua, n, n, &p, _state);
        errspec = errspec||ae_fp_neq(cmatrixrcond1(&a, n, _state),0);
        errspec = errspec||ae_fp_neq(cmatrixrcondinf(&a, n, _state),0);
        errspec = errspec||ae_fp_neq(cmatrixlurcond1(&lua, n, _state),0);
        errspec = errspec||ae_fp_neq(cmatrixlurcondinf(&lua, n, _state),0);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        for(i=0; i<=3; i++)
        {
            q50.ptr.p_double[i] = 0;
            q90.ptr.p_double[i] = 0;
        }
        for(pass=1; pass<=passcount; pass++)
        {
            cmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log(1000, _state), _state), &a, _state);
            testrcondunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
            cmatrixlu(&lua, n, n, &p, _state);
            testrcondunit_cmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            
            /*
             * 1-norm, normal
             */
            v = 1/cmatrixrcond1(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * 1-norm, LU
             */
            v = 1/cmatrixlurcond1(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Inf-norm, normal
             */
            v = 1/cmatrixrcondinf(&a, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[2] = q50.ptr.p_double[2]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[2] = q90.ptr.p_double[2]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
            
            /*
             * Inf-norm, LU
             */
            v = 1/cmatrixlurcondinf(&lua, n, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*ercinf) )
            {
                q50.ptr.p_double[3] = q50.ptr.p_double[3]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*ercinf) )
            {
                q90.ptr.p_double[3] = q90.ptr.p_double[3]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,ercinf*1.001);
        }
        for(i=0; i<=3; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            a.ptr.pp_complex[0][0] = ae_complex_from_d(1);
            a.ptr.pp_complex[n-1][n-1] = ae_complex_from_d(1);
            errspec = errspec||ae_fp_neq(cmatrixrcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixrcondinf(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixlurcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixlurcondinf(&a, n, _state),0);
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i] = ae_complex_from_d(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*ae_maxrealnumber);
            errspec = errspec||ae_fp_neq(cmatrixrcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixrcondinf(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixlurcond1(&a, n, _state),0);
            errspec = errspec||ae_fp_neq(cmatrixlurcondinf(&a, n, _state),0);
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Returns True for successful test, False - for failed test
*************************************************************************/
static ae_bool testrcondunit_testhpdmatrixrcond(ae_int_t maxn,
     ae_int_t passcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_vector p;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_bool err50;
    ae_bool err90;
    ae_bool errspec;
    ae_bool errless;
    ae_bool isupper;
    double erc1;
    double ercinf;
    ae_vector q50;
    ae_vector q90;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_vector_init(&q50, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&q90, 0, DT_REAL, _state, ae_true);

    err50 = ae_false;
    err90 = ae_false;
    errless = ae_false;
    errspec = ae_false;
    ae_vector_set_length(&q50, 2, _state);
    ae_vector_set_length(&q90, 2, _state);
    for(n=1; n<=maxn; n++)
    {
        isupper = ae_fp_greater(ae_randomreal(_state),0.5);
        
        /*
         * general test
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=1; i++)
        {
            q50.ptr.p_double[i] = 0;
            q90.ptr.p_double[i] = 0;
        }
        for(pass=1; pass<=passcount; pass++)
        {
            hpdmatrixrndcond(n, ae_exp(ae_randomreal(_state)*ae_log(1000, _state), _state), &a, _state);
            testrcondunit_cmatrixrefrcond(&a, n, &erc1, &ercinf, _state);
            testrcondunit_cmatrixdrophalf(&a, n, isupper, _state);
            testrcondunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
            hpdmatrixcholesky(&cha, n, isupper, _state);
            
            /*
             * normal
             */
            v = 1/hpdmatrixrcond(&a, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[0] = q50.ptr.p_double[0]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[0] = q90.ptr.p_double[0]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
            
            /*
             * Cholesky
             */
            v = 1/hpdmatrixcholeskyrcond(&cha, n, isupper, _state);
            if( ae_fp_greater_eq(v,testrcondunit_threshold50*erc1) )
            {
                q50.ptr.p_double[1] = q50.ptr.p_double[1]+(double)1/(double)passcount;
            }
            if( ae_fp_greater_eq(v,testrcondunit_threshold90*erc1) )
            {
                q90.ptr.p_double[1] = q90.ptr.p_double[1]+(double)1/(double)passcount;
            }
            errless = errless||ae_fp_greater(v,erc1*1.001);
        }
        for(i=0; i<=1; i++)
        {
            err50 = err50||ae_fp_less(q50.ptr.p_double[i],0.50);
            err90 = err90||ae_fp_less(q90.ptr.p_double[i],0.90);
        }
        
        /*
         * degenerate matrix test
         */
        if( n>=3 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            a.ptr.pp_complex[0][0] = ae_complex_from_d(1);
            a.ptr.pp_complex[n-1][n-1] = ae_complex_from_d(1);
            errspec = errspec||ae_fp_neq(hpdmatrixrcond(&a, n, isupper, _state),-1);
            errspec = errspec||ae_fp_neq(hpdmatrixcholeskyrcond(&a, n, isupper, _state),0);
        }
        
        /*
         * near-degenerate matrix test
         */
        if( n>=2 )
        {
            ae_matrix_set_length(&a, n, n, _state);
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_complex[i][j] = ae_complex_from_d(0.0);
                }
            }
            for(i=0; i<=n-1; i++)
            {
                a.ptr.pp_complex[i][i] = ae_complex_from_d(1);
            }
            i = ae_randominteger(n, _state);
            a.ptr.pp_complex[i][i] = ae_complex_from_d(0.1*ae_maxrealnumber);
            errspec = errspec||ae_fp_neq(hpdmatrixrcond(&a, n, isupper, _state),0);
            errspec = errspec||ae_fp_neq(hpdmatrixcholeskyrcond(&a, n, isupper, _state),0);
        }
    }
    
    /*
     * report
     */
    result = !(((err50||err90)||errless)||errspec);
    ae_frame_leave(_state);
    return result;
}



static void testmatinvunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testmatinvunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static ae_bool testmatinvunit_rmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_spdmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_hpdmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_rmatrixcheckinversesingular(/* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_cmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static ae_bool testmatinvunit_cmatrixcheckinversesingular(/* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state);
static void testmatinvunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testmatinvunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testmatinvunit_testrtrinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rtrerrors,
     ae_state *_state);
static void testmatinvunit_testctrinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* ctrerrors,
     ae_state *_state);
static void testmatinvunit_testrinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_state *_state);
static void testmatinvunit_testcinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_state *_state);
static void testmatinvunit_testspdinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_state *_state);
static void testmatinvunit_testhpdinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_state *_state);
static void testmatinvunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state);
static void testmatinvunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state);
static void testmatinvunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state);
static void testmatinvunit_cunset1d(/* Complex */ ae_vector* x,
     ae_state *_state);
static void testmatinvunit_unsetrep(matinvreport* r, ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testmatinv(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxrn;
    ae_int_t maxcn;
    ae_int_t passcount;
    double threshold;
    double rcondtol;
    ae_bool rtrerrors;
    ae_bool ctrerrors;
    ae_bool rerrors;
    ae_bool cerrors;
    ae_bool spderrors;
    ae_bool hpderrors;
    ae_bool waserrors;
    ae_matrix emptyra;
    ae_matrix emptyca;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&emptyra, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&emptyca, 0, 0, DT_REAL, _state, ae_true);

    maxrn = 3*ablasblocksize(&emptyra, _state)+1;
    maxcn = 3*ablasblocksize(&emptyca, _state)+1;
    passcount = 1;
    threshold = 10000*ae_machineepsilon;
    rcondtol = 0.01;
    rtrerrors = ae_false;
    ctrerrors = ae_false;
    rerrors = ae_false;
    cerrors = ae_false;
    spderrors = ae_false;
    hpderrors = ae_false;
    testmatinvunit_testrtrinv(maxrn, passcount, threshold, &rtrerrors, _state);
    testmatinvunit_testctrinv(maxcn, passcount, threshold, &ctrerrors, _state);
    testmatinvunit_testrinv(maxrn, passcount, threshold, &rerrors, _state);
    testmatinvunit_testspdinv(maxrn, passcount, threshold, &spderrors, _state);
    testmatinvunit_testcinv(maxcn, passcount, threshold, &cerrors, _state);
    testmatinvunit_testhpdinv(maxcn, passcount, threshold, &hpderrors, _state);
    waserrors = ((((rtrerrors||ctrerrors)||rerrors)||cerrors)||spderrors)||hpderrors;
    if( !silent )
    {
        printf("TESTING MATINV\n");
        printf("* REAL TRIANGULAR:                        ");
        if( rtrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX TRIANGULAR:                     ");
        if( ctrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* REAL:                                   ");
        if( rerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX:                                ");
        if( cerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPD:                                    ");
        if( spderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* HPD:                                    ");
        if( hpderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testmatinvunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testmatinvunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_rmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &inva->ptr.pp_double[0][j], inva->stride, ae_v_len(0,n-1));
                if( i==j )
                {
                    v = v-1;
                }
                result = result&&ae_fp_less_eq(ae_fabs(v, _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_spdmatrixcheckinverse(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_matrix _inva;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;
    ae_matrix_init_copy(&_inva, inva, _state, ae_true);
    inva = &_inva;

    for(i=0; i<=n-2; i++)
    {
        if( isupper )
        {
            ae_v_move(&a->ptr.pp_double[i+1][i], a->stride, &a->ptr.pp_double[i][i+1], 1, ae_v_len(i+1,n-1));
            ae_v_move(&inva->ptr.pp_double[i+1][i], inva->stride, &inva->ptr.pp_double[i][i+1], 1, ae_v_len(i+1,n-1));
        }
        else
        {
            ae_v_move(&a->ptr.pp_double[i][i+1], 1, &a->ptr.pp_double[i+1][i], a->stride, ae_v_len(i+1,n-1));
            ae_v_move(&inva->ptr.pp_double[i][i+1], 1, &inva->ptr.pp_double[i+1][i], inva->stride, ae_v_len(i+1,n-1));
        }
    }
    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &inva->ptr.pp_double[0][j], inva->stride, ae_v_len(0,n-1));
                if( i==j )
                {
                    v = v-1;
                }
                result = result&&ae_fp_less_eq(ae_fabs(v, _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_hpdmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_bool isupper,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix _a;
    ae_matrix _inva;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init_copy(&_a, a, _state, ae_true);
    a = &_a;
    ae_matrix_init_copy(&_inva, inva, _state, ae_true);
    inva = &_inva;

    for(i=0; i<=n-2; i++)
    {
        if( isupper )
        {
            ae_v_cmove(&a->ptr.pp_complex[i+1][i], a->stride, &a->ptr.pp_complex[i][i+1], 1, "Conj", ae_v_len(i+1,n-1));
            ae_v_cmove(&inva->ptr.pp_complex[i+1][i], inva->stride, &inva->ptr.pp_complex[i][i+1], 1, "Conj", ae_v_len(i+1,n-1));
        }
        else
        {
            ae_v_cmove(&a->ptr.pp_complex[i][i+1], 1, &a->ptr.pp_complex[i+1][i], a->stride, "Conj", ae_v_len(i+1,n-1));
            ae_v_cmove(&inva->ptr.pp_complex[i][i+1], 1, &inva->ptr.pp_complex[i+1][i], inva->stride, "Conj", ae_v_len(i+1,n-1));
        }
    }
    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[i][0], 1, "N", &inva->ptr.pp_complex[0][j], inva->stride, "N", ae_v_len(0,n-1));
                if( i==j )
                {
                    v = ae_c_sub_d(v,1);
                }
                result = result&&ae_fp_less_eq(ae_c_abs(v, _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether inversion result indicate singular matrix
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_rmatrixcheckinversesingular(/* Real    */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,0)||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,0)||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
        if( info==-3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = result&&ae_fp_eq(inva->ptr.pp_double[i][j],0);
                }
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether inverse is correct
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_cmatrixcheckinverse(/* Complex */ ae_matrix* a,
     /* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                v = ae_v_cdotproduct(&a->ptr.pp_complex[i][0], 1, "N", &inva->ptr.pp_complex[0][j], inva->stride, "N", ae_v_len(0,n-1));
                if( i==j )
                {
                    v = ae_c_sub_d(v,1);
                }
                result = result&&ae_fp_less_eq(ae_c_abs(v, _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether inversion result indicate singular matrix
Returns True on success.
*************************************************************************/
static ae_bool testmatinvunit_cmatrixcheckinversesingular(/* Complex */ ae_matrix* inva,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     matinvreport* rep,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,0)||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,0)||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
        if( info==-3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    result = result&&ae_c_eq_d(inva->ptr.pp_complex[i][j],0);
                }
            }
        }
    }
    return result;
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testmatinvunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_double[i][j] = 1+2*i+3*j;
            }
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testmatinvunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_d(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Real TR inverse
*************************************************************************/
static void testmatinvunit_testrtrinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rtrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix b;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t task;
    ae_bool isupper;
    ae_bool isunit;
    double v;
    ae_bool waserrors;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    
    /*
     * Test
     */
    for(n=1; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, n, _state);
        for(task=0; task<=3; task++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Determine task
                 */
                isupper = task%2==0;
                isunit = task/2%2==0;
                
                /*
                 * Generate matrix
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            a.ptr.pp_double[i][i] = 1+ae_randomreal(_state);
                        }
                        else
                        {
                            a.ptr.pp_double[i][j] = 0.2*ae_randomreal(_state)-0.1;
                        }
                        b.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                    }
                }
                
                /*
                 * Inverse
                 */
                rmatrixtrinverse(&b, n, isupper, isunit, &info, &rep, _state);
                if( info<=0 )
                {
                    *rtrerrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Structural test
                 */
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *rtrerrors = *rtrerrors||ae_fp_neq(a.ptr.pp_double[i][i],b.ptr.pp_double[i][i]);
                    }
                }
                if( isupper )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=i-1; j++)
                        {
                            *rtrerrors = *rtrerrors||ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                        }
                    }
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            *rtrerrors = *rtrerrors||ae_fp_neq(a.ptr.pp_double[i][j],b.ptr.pp_double[i][j]);
                        }
                    }
                }
                
                /*
                 * Inverse test
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            a.ptr.pp_double[i][j] = 0;
                            b.ptr.pp_double[i][j] = 0;
                        }
                    }
                }
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a.ptr.pp_double[i][i] = 1;
                        b.ptr.pp_double[i][i] = 1;
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &b.ptr.pp_double[0][j], b.stride, ae_v_len(0,n-1));
                        if( j!=i )
                        {
                            *rtrerrors = *rtrerrors||ae_fp_greater(ae_fabs(v, _state),threshold);
                        }
                        else
                        {
                            *rtrerrors = *rtrerrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Complex TR inverse
*************************************************************************/
static void testmatinvunit_testctrinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* ctrerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix b;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t task;
    ae_bool isupper;
    ae_bool isunit;
    ae_complex v;
    ae_bool waserrors;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    
    /*
     * Test
     */
    for(n=1; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, n, _state);
        for(task=0; task<=3; task++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Determine task
                 */
                isupper = task%2==0;
                isunit = task/2%2==0;
                
                /*
                 * Generate matrix
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( i==j )
                        {
                            a.ptr.pp_complex[i][i].x = 1+ae_randomreal(_state);
                            a.ptr.pp_complex[i][i].y = 1+ae_randomreal(_state);
                        }
                        else
                        {
                            a.ptr.pp_complex[i][j].x = 0.2*ae_randomreal(_state)-0.1;
                            a.ptr.pp_complex[i][j].y = 0.2*ae_randomreal(_state)-0.1;
                        }
                        b.ptr.pp_complex[i][j] = a.ptr.pp_complex[i][j];
                    }
                }
                
                /*
                 * Inverse
                 */
                cmatrixtrinverse(&b, n, isupper, isunit, &info, &rep, _state);
                if( info<=0 )
                {
                    *ctrerrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                
                /*
                 * Structural test
                 */
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *ctrerrors = *ctrerrors||ae_c_neq(a.ptr.pp_complex[i][i],b.ptr.pp_complex[i][i]);
                    }
                }
                if( isupper )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=i-1; j++)
                        {
                            *ctrerrors = *ctrerrors||ae_c_neq(a.ptr.pp_complex[i][j],b.ptr.pp_complex[i][j]);
                        }
                    }
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            *ctrerrors = *ctrerrors||ae_c_neq(a.ptr.pp_complex[i][j],b.ptr.pp_complex[i][j]);
                        }
                    }
                }
                
                /*
                 * Inverse test
                 */
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( (j<i&&isupper)||(j>i&&!isupper) )
                        {
                            a.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                            b.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                        }
                    }
                }
                if( isunit )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        a.ptr.pp_complex[i][i] = ae_complex_from_d(1);
                        b.ptr.pp_complex[i][i] = ae_complex_from_d(1);
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &b.ptr.pp_complex[0][j], b.stride, "N", ae_v_len(0,n-1));
                        if( j!=i )
                        {
                            *ctrerrors = *ctrerrors||ae_fp_greater(ae_c_abs(v, _state),threshold);
                        }
                        else
                        {
                            *ctrerrors = *ctrerrors||ae_fp_greater(ae_c_abs(ae_c_sub_d(v,1), _state),threshold);
                        }
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Real test
*************************************************************************/
static void testmatinvunit_testrinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix inva;
    ae_matrix invlua;
    ae_vector p;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&invlua, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            rmatrixrndcond(n, 1000, &a, _state);
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
            rmatrixlu(&lua, n, n, &p, _state);
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_rmatrixmakeacopy(&lua, n, n, &invlua, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            rmatrixinverse(&inva, n, &info, &rep, _state);
            *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinverse(&a, &inva, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            rmatrixluinverse(&invlua, &p, n, &info, &rep, _state);
            *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinverse(&a, &invlua, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             *    * with equal rows/columns
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=4; taskkind++)
            {
                testmatinvunit_unset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0;
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                }
                if( taskkind==3 )
                {
                    
                    /*
                     * equal columns
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_move(&a.ptr.pp_double[0][0], a.stride, &a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1));
                }
                if( taskkind==4 )
                {
                    
                    /*
                     * equal rows
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_move(&a.ptr.pp_double[0][0], 1, &a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1));
                }
                testmatinvunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
                rmatrixlu(&lua, n, n, &p, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                rmatrixinverse(&a, n, &info, &rep, _state);
                *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinversesingular(&a, n, threshold, info, &rep, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                rmatrixluinverse(&lua, &p, n, &info, &rep, _state);
                *rerrors = *rerrors||!testmatinvunit_rmatrixcheckinversesingular(&lua, n, threshold, info, &rep, _state);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Complex test
*************************************************************************/
static void testmatinvunit_testcinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix inva;
    ae_matrix invlua;
    ae_vector p;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&invlua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            cmatrixrndcond(n, 1000, &a, _state);
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
            cmatrixlu(&lua, n, n, &p, _state);
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_cmatrixmakeacopy(&lua, n, n, &invlua, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            cmatrixinverse(&inva, n, &info, &rep, _state);
            *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinverse(&a, &inva, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            cmatrixluinverse(&invlua, &p, n, &info, &rep, _state);
            *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinverse(&a, &invlua, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             *    * with equal rows/columns
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=4; taskkind++)
            {
                testmatinvunit_cunset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                }
                if( taskkind==3 )
                {
                    
                    /*
                     * equal columns
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_cmove(&a.ptr.pp_complex[0][0], a.stride, &a.ptr.pp_complex[0][k], a.stride, "N", ae_v_len(0,n-1));
                }
                if( taskkind==4 )
                {
                    
                    /*
                     * equal rows
                     */
                    if( n<2 )
                    {
                        continue;
                    }
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = 1+ae_randominteger(n-1, _state);
                    ae_v_cmove(&a.ptr.pp_complex[0][0], 1, &a.ptr.pp_complex[k][0], 1, "N", ae_v_len(0,n-1));
                }
                testmatinvunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
                cmatrixlu(&lua, n, n, &p, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                cmatrixinverse(&a, n, &info, &rep, _state);
                *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinversesingular(&a, n, threshold, info, &rep, _state);
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                cmatrixluinverse(&lua, &p, n, &info, &rep, _state);
                *cerrors = *cerrors||!testmatinvunit_cmatrixcheckinversesingular(&lua, n, threshold, info, &rep, _state);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
SPD test
*************************************************************************/
static void testmatinvunit_testspdinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix inva;
    ae_matrix invcha;
    ae_bool isupper;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&invcha, 0, 0, DT_REAL, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            spdmatrixrndcond(n, 1000, &a, _state);
            testmatinvunit_rmatrixdrophalf(&a, n, isupper, _state);
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
            if( !spdmatrixcholesky(&cha, n, isupper, _state) )
            {
                continue;
            }
            testmatinvunit_rmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_rmatrixmakeacopy(&cha, n, n, &invcha, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            spdmatrixinverse(&inva, n, isupper, &info, &rep, _state);
            *spderrors = *spderrors||!testmatinvunit_spdmatrixcheckinverse(&a, &inva, isupper, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            spdmatrixcholeskyinverse(&invcha, n, isupper, &info, &rep, _state);
            *spderrors = *spderrors||!testmatinvunit_spdmatrixcheckinverse(&a, &invcha, isupper, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=2; taskkind++)
            {
                testmatinvunit_unset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 0;
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                }
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                spdmatrixcholeskyinverse(&a, n, isupper, &info, &rep, _state);
                if( info!=-3&&info!=1 )
                {
                    *spderrors = ae_true;
                }
                else
                {
                    *spderrors = (*spderrors||ae_fp_less(rep.r1,0))||ae_fp_greater(rep.r1,1000*ae_machineepsilon);
                    *spderrors = (*spderrors||ae_fp_less(rep.rinf,0))||ae_fp_greater(rep.rinf,1000*ae_machineepsilon);
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
HPD test
*************************************************************************/
static void testmatinvunit_testhpdinv(ae_int_t maxn,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix inva;
    ae_matrix invcha;
    ae_bool isupper;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_int_t info;
    matinvreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&invcha, 0, 0, DT_COMPLEX, _state, ae_true);
    _matinvreport_init(&rep, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            isupper = ae_fp_greater(ae_randomreal(_state),0.5);
            
            /*
             * ********************************************************
             * WELL CONDITIONED TASKS
             * ability to find correct solution is tested
             * ********************************************************
             *
             * 1. generate random well conditioned matrix A.
             * 2. generate random solution vector xe
             * 3. generate right part b=A*xe
             * 4. test different methods on original A
             */
            hpdmatrixrndcond(n, 1000, &a, _state);
            testmatinvunit_cmatrixdrophalf(&a, n, isupper, _state);
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
            if( !hpdmatrixcholesky(&cha, n, isupper, _state) )
            {
                continue;
            }
            testmatinvunit_cmatrixmakeacopy(&a, n, n, &inva, _state);
            testmatinvunit_cmatrixmakeacopy(&cha, n, n, &invcha, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            hpdmatrixinverse(&inva, n, isupper, &info, &rep, _state);
            *hpderrors = *hpderrors||!testmatinvunit_hpdmatrixcheckinverse(&a, &inva, isupper, n, threshold, info, &rep, _state);
            info = 0;
            testmatinvunit_unsetrep(&rep, _state);
            hpdmatrixcholeskyinverse(&invcha, n, isupper, &info, &rep, _state);
            *hpderrors = *hpderrors||!testmatinvunit_hpdmatrixcheckinverse(&a, &invcha, isupper, n, threshold, info, &rep, _state);
            
            /*
             * ********************************************************
             * EXACTLY SINGULAR MATRICES
             * ability to detect singularity is tested
             * ********************************************************
             *
             * 1. generate different types of singular matrices:
             *    * zero
             *    * with zero columns
             *    * with zero rows
             * 2. test different methods
             */
            for(taskkind=0; taskkind<=2; taskkind++)
            {
                testmatinvunit_cunset2d(&a, _state);
                if( taskkind==0 )
                {
                    
                    /*
                     * all zeros
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                        }
                    }
                }
                if( taskkind==1 )
                {
                    
                    /*
                     * there is zero column
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                    ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                }
                if( taskkind==2 )
                {
                    
                    /*
                     * there is zero row
                     */
                    ae_matrix_set_length(&a, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                        }
                    }
                    k = ae_randominteger(n, _state);
                    ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                    ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                }
                info = 0;
                testmatinvunit_unsetrep(&rep, _state);
                hpdmatrixcholeskyinverse(&a, n, isupper, &info, &rep, _state);
                if( info!=-3&&info!=1 )
                {
                    *hpderrors = ae_true;
                }
                else
                {
                    *hpderrors = (*hpderrors||ae_fp_less(rep.r1,0))||ae_fp_greater(rep.r1,1000*ae_machineepsilon);
                    *hpderrors = (*hpderrors||ae_fp_less(rep.rinf,0))||ae_fp_greater(rep.rinf,1000*ae_machineepsilon);
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testmatinvunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testmatinvunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testmatinvunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testmatinvunit_cunset1d(/* Complex */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_complex[0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets report
*************************************************************************/
static void testmatinvunit_unsetrep(matinvreport* r, ae_state *_state)
{


    r->r1 = -1;
    r->rinf = -1;
}



static void testldaunit_gensimpleset(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state);
static void testldaunit_gendeg1set(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state);
static double testldaunit_generatenormal(double mean,
     double sigma,
     ae_state *_state);
static ae_bool testldaunit_testwn(/* Real    */ ae_matrix* xy,
     /* Real    */ ae_matrix* wn,
     ae_int_t ns,
     ae_int_t nf,
     ae_int_t nc,
     ae_int_t ndeg,
     ae_state *_state);
static double testldaunit_calcj(ae_int_t nf,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     /* Real    */ ae_vector* w,
     double* p,
     double* q,
     ae_state *_state);
static void testldaunit_fishers(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nfeatures,
     ae_int_t nclasses,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     ae_state *_state);





ae_bool testlda(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t maxnf;
    ae_int_t maxns;
    ae_int_t maxnc;
    ae_int_t passcount;
    ae_bool ldanerrors;
    ae_bool lda1errors;
    ae_bool waserrors;
    ae_int_t nf;
    ae_int_t nc;
    ae_int_t ns;
    ae_int_t i;
    ae_int_t info;
    ae_int_t pass;
    ae_int_t axis;
    ae_matrix xy;
    ae_matrix wn;
    ae_vector w1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&wn, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w1, 0, DT_REAL, _state, ae_true);

    
    /*
     * Primary settings
     */
    maxnf = 10;
    maxns = 1000;
    maxnc = 5;
    passcount = 1;
    waserrors = ae_false;
    ldanerrors = ae_false;
    lda1errors = ae_false;
    
    /*
     * General tests
     */
    for(nf=1; nf<=maxnf; nf++)
    {
        for(nc=2; nc<=maxnc; nc++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Simple test for LDA-N/LDA-1
                 */
                axis = ae_randominteger(nf, _state);
                ns = maxns/2+ae_randominteger(maxns/2, _state);
                testldaunit_gensimpleset(nf, nc, ns, axis, &xy, _state);
                fisherldan(&xy, ns, nf, nc, &info, &wn, _state);
                if( info!=1 )
                {
                    ldanerrors = ae_true;
                    continue;
                }
                ldanerrors = ldanerrors||!testldaunit_testwn(&xy, &wn, ns, nf, nc, 0, _state);
                ldanerrors = ldanerrors||ae_fp_less_eq(ae_fabs(wn.ptr.pp_double[axis][0], _state),0.75);
                fisherlda(&xy, ns, nf, nc, &info, &w1, _state);
                for(i=0; i<=nf-1; i++)
                {
                    lda1errors = lda1errors||ae_fp_neq(w1.ptr.p_double[i],wn.ptr.pp_double[i][0]);
                }
                
                /*
                 * Degenerate test for LDA-N
                 */
                if( nf>=3 )
                {
                    ns = maxns/2+ae_randominteger(maxns/2, _state);
                    
                    /*
                     * there are two duplicate features,
                     * axis is oriented along non-duplicate feature
                     */
                    axis = ae_randominteger(nf-2, _state);
                    testldaunit_gendeg1set(nf, nc, ns, axis, &xy, _state);
                    fisherldan(&xy, ns, nf, nc, &info, &wn, _state);
                    if( info!=2 )
                    {
                        ldanerrors = ae_true;
                        continue;
                    }
                    ldanerrors = ldanerrors||ae_fp_less_eq(wn.ptr.pp_double[axis][0],0.75);
                    fisherlda(&xy, ns, nf, nc, &info, &w1, _state);
                    for(i=0; i<=nf-1; i++)
                    {
                        lda1errors = lda1errors||ae_fp_neq(w1.ptr.p_double[i],wn.ptr.pp_double[i][0]);
                    }
                }
            }
        }
    }
    
    /*
     * Final report
     */
    waserrors = ldanerrors||lda1errors;
    if( !silent )
    {
        printf("LDA TEST\n");
        printf("FISHER LDA-N:                            ");
        if( !ldanerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("FISHER LDA-1:                            ");
        if( !lda1errors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Generates 'simple' set - a sequence of unit 'balls' at (0,0), (1,0), (2,0)
and so on.
*************************************************************************/
static void testldaunit_gensimpleset(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t c;

    ae_matrix_clear(xy);

    ae_assert(axis>=0&&axis<nfeatures, "GenSimpleSet: wrong Axis!", _state);
    ae_matrix_set_length(xy, nsamples-1+1, nfeatures+1, _state);
    for(i=0; i<=nsamples-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            xy->ptr.pp_double[i][j] = testldaunit_generatenormal(0.0, 1.0, _state);
        }
        c = i%nclasses;
        xy->ptr.pp_double[i][axis] = xy->ptr.pp_double[i][axis]+c;
        xy->ptr.pp_double[i][nfeatures] = c;
    }
}


/*************************************************************************
Generates 'degenerate' set #1.
NFeatures>=3.
*************************************************************************/
static void testldaunit_gendeg1set(ae_int_t nfeatures,
     ae_int_t nclasses,
     ae_int_t nsamples,
     ae_int_t axis,
     /* Real    */ ae_matrix* xy,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t c;

    ae_matrix_clear(xy);

    ae_assert(axis>=0&&axis<nfeatures, "GenDeg1Set: wrong Axis!", _state);
    ae_assert(nfeatures>=3, "GenDeg1Set: wrong NFeatures!", _state);
    ae_matrix_set_length(xy, nsamples-1+1, nfeatures+1, _state);
    if( axis>=nfeatures-2 )
    {
        axis = nfeatures-3;
    }
    for(i=0; i<=nsamples-1; i++)
    {
        for(j=0; j<=nfeatures-2; j++)
        {
            xy->ptr.pp_double[i][j] = testldaunit_generatenormal(0.0, 1.0, _state);
        }
        xy->ptr.pp_double[i][nfeatures-1] = xy->ptr.pp_double[i][nfeatures-2];
        c = i%nclasses;
        xy->ptr.pp_double[i][axis] = xy->ptr.pp_double[i][axis]+c;
        xy->ptr.pp_double[i][nfeatures] = c;
    }
}


/*************************************************************************
Normal random number
*************************************************************************/
static double testldaunit_generatenormal(double mean,
     double sigma,
     ae_state *_state)
{
    double u;
    double v;
    double sum;
    double result;


    result = mean;
    for(;;)
    {
        u = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        v = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        sum = u*u+v*v;
        if( ae_fp_less(sum,1)&&ae_fp_greater(sum,0) )
        {
            sum = ae_sqrt(-2*ae_log(sum, _state)/sum, _state);
            result = sigma*u*sum+mean;
            return result;
        }
    }
    return result;
}


/*************************************************************************
Tests WN for correctness
*************************************************************************/
static ae_bool testldaunit_testwn(/* Real    */ ae_matrix* xy,
     /* Real    */ ae_matrix* wn,
     ae_int_t ns,
     ae_int_t nf,
     ae_int_t nc,
     ae_int_t ndeg,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix st;
    ae_matrix sw;
    ae_matrix a;
    ae_matrix z;
    ae_vector tx;
    ae_vector jp;
    ae_vector jq;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    double v;
    double wprev;
    double tol;
    double p;
    double q;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&st, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&sw, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&jp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&jq, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    tol = 10000;
    result = ae_true;
    testldaunit_fishers(xy, ns, nf, nc, &st, &sw, _state);
    
    /*
     * Test for decreasing of J
     */
    ae_vector_set_length(&tx, nf-1+1, _state);
    ae_vector_set_length(&jp, nf-1+1, _state);
    ae_vector_set_length(&jq, nf-1+1, _state);
    for(j=0; j<=nf-1; j++)
    {
        ae_v_move(&tx.ptr.p_double[0], 1, &wn->ptr.pp_double[0][j], wn->stride, ae_v_len(0,nf-1));
        v = testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state);
        jp.ptr.p_double[j] = p;
        jq.ptr.p_double[j] = q;
    }
    for(i=1; i<=nf-1-ndeg; i++)
    {
        result = result&&ae_fp_greater_eq(jp.ptr.p_double[i-1]/jq.ptr.p_double[i-1],(1-tol*ae_machineepsilon)*jp.ptr.p_double[i]/jq.ptr.p_double[i]);
    }
    for(i=nf-1-ndeg+1; i<=nf-1; i++)
    {
        result = result&&ae_fp_less_eq(jp.ptr.p_double[i],tol*ae_machineepsilon*jp.ptr.p_double[0]);
    }
    
    /*
     * Test for J optimality
     */
    ae_v_move(&tx.ptr.p_double[0], 1, &wn->ptr.pp_double[0][0], wn->stride, ae_v_len(0,nf-1));
    v = testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state);
    for(i=0; i<=nf-1; i++)
    {
        wprev = tx.ptr.p_double[i];
        tx.ptr.p_double[i] = wprev+0.01;
        result = result&&ae_fp_greater_eq(v,(1-tol*ae_machineepsilon)*testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state));
        tx.ptr.p_double[i] = wprev-0.01;
        result = result&&ae_fp_greater_eq(v,(1-tol*ae_machineepsilon)*testldaunit_calcj(nf, &st, &sw, &tx, &p, &q, _state));
        tx.ptr.p_double[i] = wprev;
    }
    
    /*
     * Test for linear independence of W
     */
    ae_vector_set_length(&work, nf+1, _state);
    ae_matrix_set_length(&a, nf-1+1, nf-1+1, _state);
    matrixmatrixmultiply(wn, 0, nf-1, 0, nf-1, ae_false, wn, 0, nf-1, 0, nf-1, ae_true, 1.0, &a, 0, nf-1, 0, nf-1, 0.0, &work, _state);
    if( smatrixevd(&a, nf, 1, ae_true, &tx, &z, _state) )
    {
        result = result&&ae_fp_greater(tx.ptr.p_double[0],tx.ptr.p_double[nf-1]*1000*ae_machineepsilon);
    }
    
    /*
     * Test for other properties
     */
    for(j=0; j<=nf-1; j++)
    {
        v = ae_v_dotproduct(&wn->ptr.pp_double[0][j], wn->stride, &wn->ptr.pp_double[0][j], wn->stride, ae_v_len(0,nf-1));
        v = ae_sqrt(v, _state);
        result = result&&ae_fp_less_eq(ae_fabs(v-1, _state),1000*ae_machineepsilon);
        v = 0;
        for(i=0; i<=nf-1; i++)
        {
            v = v+wn->ptr.pp_double[i][j];
        }
        result = result&&ae_fp_greater_eq(v,0);
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Calculates J
*************************************************************************/
static double testldaunit_calcj(ae_int_t nf,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     /* Real    */ ae_vector* w,
     double* p,
     double* q,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector tx;
    ae_int_t i;
    double v;
    double result;

    ae_frame_make(_state, &_frame_block);
    *p = 0;
    *q = 0;
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&tx, nf-1+1, _state);
    for(i=0; i<=nf-1; i++)
    {
        v = ae_v_dotproduct(&st->ptr.pp_double[i][0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,nf-1));
        tx.ptr.p_double[i] = v;
    }
    v = ae_v_dotproduct(&w->ptr.p_double[0], 1, &tx.ptr.p_double[0], 1, ae_v_len(0,nf-1));
    *p = v;
    for(i=0; i<=nf-1; i++)
    {
        v = ae_v_dotproduct(&sw->ptr.pp_double[i][0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,nf-1));
        tx.ptr.p_double[i] = v;
    }
    v = ae_v_dotproduct(&w->ptr.p_double[0], 1, &tx.ptr.p_double[0], 1, ae_v_len(0,nf-1));
    *q = v;
    result = *p/(*q);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Calculates ST/SW
*************************************************************************/
static void testldaunit_fishers(/* Real    */ ae_matrix* xy,
     ae_int_t npoints,
     ae_int_t nfeatures,
     ae_int_t nclasses,
     /* Real    */ ae_matrix* st,
     /* Real    */ ae_matrix* sw,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    ae_vector c;
    ae_vector mu;
    ae_matrix muc;
    ae_vector nc;
    ae_vector tf;
    ae_vector work;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_clear(st);
    ae_matrix_clear(sw);
    ae_vector_init(&c, 0, DT_INT, _state, ae_true);
    ae_vector_init(&mu, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&muc, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&nc, 0, DT_INT, _state, ae_true);
    ae_vector_init(&tf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    
    /*
     * Prepare temporaries
     */
    ae_vector_set_length(&tf, nfeatures-1+1, _state);
    ae_vector_set_length(&work, nfeatures+1, _state);
    
    /*
     * Convert class labels from reals to integers (just for convenience)
     */
    ae_vector_set_length(&c, npoints-1+1, _state);
    for(i=0; i<=npoints-1; i++)
    {
        c.ptr.p_int[i] = ae_round(xy->ptr.pp_double[i][nfeatures], _state);
    }
    
    /*
     * Calculate class sizes and means
     */
    ae_vector_set_length(&mu, nfeatures-1+1, _state);
    ae_matrix_set_length(&muc, nclasses-1+1, nfeatures-1+1, _state);
    ae_vector_set_length(&nc, nclasses-1+1, _state);
    for(j=0; j<=nfeatures-1; j++)
    {
        mu.ptr.p_double[j] = 0;
    }
    for(i=0; i<=nclasses-1; i++)
    {
        nc.ptr.p_int[i] = 0;
        for(j=0; j<=nfeatures-1; j++)
        {
            muc.ptr.pp_double[i][j] = 0;
        }
    }
    for(i=0; i<=npoints-1; i++)
    {
        ae_v_add(&mu.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nfeatures-1));
        ae_v_add(&muc.ptr.pp_double[c.ptr.p_int[i]][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nfeatures-1));
        nc.ptr.p_int[c.ptr.p_int[i]] = nc.ptr.p_int[c.ptr.p_int[i]]+1;
    }
    for(i=0; i<=nclasses-1; i++)
    {
        v = (double)1/(double)nc.ptr.p_int[i];
        ae_v_muld(&muc.ptr.pp_double[i][0], 1, ae_v_len(0,nfeatures-1), v);
    }
    v = (double)1/(double)npoints;
    ae_v_muld(&mu.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1), v);
    
    /*
     * Create ST matrix
     */
    ae_matrix_set_length(st, nfeatures-1+1, nfeatures-1+1, _state);
    for(i=0; i<=nfeatures-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            st->ptr.pp_double[i][j] = 0;
        }
    }
    for(k=0; k<=npoints-1; k++)
    {
        ae_v_move(&tf.ptr.p_double[0], 1, &xy->ptr.pp_double[k][0], 1, ae_v_len(0,nfeatures-1));
        ae_v_sub(&tf.ptr.p_double[0], 1, &mu.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1));
        for(i=0; i<=nfeatures-1; i++)
        {
            v = tf.ptr.p_double[i];
            ae_v_addd(&st->ptr.pp_double[i][0], 1, &tf.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1), v);
        }
    }
    
    /*
     * Create SW matrix
     */
    ae_matrix_set_length(sw, nfeatures-1+1, nfeatures-1+1, _state);
    for(i=0; i<=nfeatures-1; i++)
    {
        for(j=0; j<=nfeatures-1; j++)
        {
            sw->ptr.pp_double[i][j] = 0;
        }
    }
    for(k=0; k<=npoints-1; k++)
    {
        ae_v_move(&tf.ptr.p_double[0], 1, &xy->ptr.pp_double[k][0], 1, ae_v_len(0,nfeatures-1));
        ae_v_sub(&tf.ptr.p_double[0], 1, &muc.ptr.pp_double[c.ptr.p_int[k]][0], 1, ae_v_len(0,nfeatures-1));
        for(i=0; i<=nfeatures-1; i++)
        {
            v = tf.ptr.p_double[i];
            ae_v_addd(&sw->ptr.pp_double[i][0], 1, &tf.ptr.p_double[0], 1, ae_v_len(0,nfeatures-1), v);
        }
    }
    ae_frame_leave(_state);
}








ae_bool testgammafunc(ae_bool silent, ae_state *_state)
{
    double threshold;
    double v;
    double s;
    ae_bool waserrors;
    ae_bool gammaerrors;
    ae_bool lngammaerrors;
    ae_bool result;


    gammaerrors = ae_false;
    lngammaerrors = ae_false;
    waserrors = ae_false;
    threshold = 100*ae_machineepsilon;
    
    /*
     *
     */
    gammaerrors = gammaerrors||ae_fp_greater(ae_fabs(gammafunction(0.5, _state)-ae_sqrt(ae_pi, _state), _state),threshold);
    gammaerrors = gammaerrors||ae_fp_greater(ae_fabs(gammafunction(1.5, _state)-0.5*ae_sqrt(ae_pi, _state), _state),threshold);
    v = lngamma(0.5, &s, _state);
    lngammaerrors = (lngammaerrors||ae_fp_greater(ae_fabs(v-ae_log(ae_sqrt(ae_pi, _state), _state), _state),threshold))||ae_fp_neq(s,1);
    v = lngamma(1.5, &s, _state);
    lngammaerrors = (lngammaerrors||ae_fp_greater(ae_fabs(v-ae_log(0.5*ae_sqrt(ae_pi, _state), _state), _state),threshold))||ae_fp_neq(s,1);
    
    /*
     * report
     */
    waserrors = gammaerrors||lngammaerrors;
    if( !silent )
    {
        printf("TESTING GAMMA FUNCTION\n");
        printf("GAMMA:                                   ");
        if( gammaerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LN GAMMA:                                ");
        if( lngammaerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    return result;
}



static void testbdsvdunit_fillidentity(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static void testbdsvdunit_fillsparsede(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testbdsvdunit_getbdsvderror(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool isupper,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state);
static void testbdsvdunit_testbdsvdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state);





/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testbdsvd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector d;
    ae_vector e;
    ae_matrix mempty;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t pass;
    ae_bool waserrors;
    ae_bool wsorted;
    ae_bool wfailed;
    ae_bool failcase;
    double materr;
    double orterr;
    double threshold;
    double failthreshold;
    double failr;
    ae_int_t failcount;
    ae_int_t succcount;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&e, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&mempty, 0, 0, DT_REAL, _state, ae_true);

    failcount = 0;
    succcount = 0;
    materr = 0;
    orterr = 0;
    wsorted = ae_true;
    wfailed = ae_false;
    waserrors = ae_false;
    maxn = 15;
    threshold = 5*100*ae_machineepsilon;
    failthreshold = 1.0E-2;
    ae_vector_set_length(&d, maxn-1+1, _state);
    ae_vector_set_length(&e, maxn-2+1, _state);
    
    /*
     * special case: fail matrix
     */
    n = 5;
    d.ptr.p_double[0] = -8.27448347422711894000e-01;
    d.ptr.p_double[1] = -8.16705832087160854600e-01;
    d.ptr.p_double[2] = -2.53974358904729382800e-17;
    d.ptr.p_double[3] = -1.24626684881972815700e+00;
    d.ptr.p_double[4] = -4.64744131545637651000e-01;
    e.ptr.p_double[0] = -3.25785088656270038800e-01;
    e.ptr.p_double[1] = -1.03732413708914436580e-01;
    e.ptr.p_double[2] = -9.57365642262031357700e-02;
    e.ptr.p_double[3] = -2.71564153973817390400e-01;
    failcase = rmatrixbdsvd(&d, &e, n, ae_true, ae_false, &mempty, 0, &mempty, 0, &mempty, 0, _state);
    
    /*
     * special case: zero divide matrix
     * unfixed LAPACK routine should fail on this problem
     */
    n = 7;
    d.ptr.p_double[0] = -6.96462904751731892700e-01;
    d.ptr.p_double[1] = 0.00000000000000000000e+00;
    d.ptr.p_double[2] = -5.73827770385971991400e-01;
    d.ptr.p_double[3] = -6.62562624399371191700e-01;
    d.ptr.p_double[4] = 5.82737148001782223600e-01;
    d.ptr.p_double[5] = 3.84825263580925003300e-01;
    d.ptr.p_double[6] = 9.84087420830525472200e-01;
    e.ptr.p_double[0] = -7.30307931760612871800e-02;
    e.ptr.p_double[1] = -2.30079042939542843800e-01;
    e.ptr.p_double[2] = -6.87824621739351216300e-01;
    e.ptr.p_double[3] = -1.77306437707837570600e-02;
    e.ptr.p_double[4] = 1.78285126526551632000e-15;
    e.ptr.p_double[5] = -4.89434737751289969400e-02;
    rmatrixbdsvd(&d, &e, n, ae_true, ae_false, &mempty, 0, &mempty, 0, &mempty, 0, _state);
    
    /*
     * zero matrix, several cases
     */
    for(i=0; i<=maxn-1; i++)
    {
        d.ptr.p_double[i] = 0;
    }
    for(i=0; i<=maxn-2; i++)
    {
        e.ptr.p_double[i] = 0;
    }
    for(n=1; n<=maxn; n++)
    {
        testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
    }
    
    /*
     * Dense matrix
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            for(i=0; i<=maxn-1; i++)
            {
                d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=maxn-2; i++)
            {
                e.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
        }
    }
    
    /*
     * Sparse matrices, very sparse matrices, incredible sparse matrices
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            testbdsvdunit_fillsparsede(&d, &e, n, 0.5, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
            testbdsvdunit_fillsparsede(&d, &e, n, 0.8, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
            testbdsvdunit_fillsparsede(&d, &e, n, 0.9, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
            testbdsvdunit_fillsparsede(&d, &e, n, 0.95, _state);
            testbdsvdunit_testbdsvdproblem(&d, &e, n, &materr, &orterr, &wsorted, &wfailed, &failcount, &succcount, _state);
        }
    }
    
    /*
     * report
     */
    failr = (double)failcount/(double)(succcount+failcount);
    waserrors = ((ae_fp_greater(materr,threshold)||ae_fp_greater(orterr,threshold))||!wsorted)||ae_fp_greater(failr,failthreshold);
    if( !silent )
    {
        printf("TESTING BIDIAGONAL SVD DECOMPOSITION\n");
        printf("SVD decomposition error:                 %5.3e\n",
            (double)(materr));
        printf("SVD orthogonality error:                 %5.3e\n",
            (double)(orterr));
        printf("Singular values order:                   ");
        if( wsorted )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
            printf("Fail ratio:                              %5.3f\n",
                (double)(failr));
        }
        printf("Fail matrix test:                        ");
        if( !failcase )
        {
            printf("AS EXPECTED\n");
        }
        else
        {
            printf("CONVERGED (UNEXPECTED)\n");
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testbdsvdunit_fillidentity(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    ae_matrix_set_length(a, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( i==j )
            {
                a->ptr.pp_double[i][j] = 1;
            }
            else
            {
                a->ptr.pp_double[i][j] = 0;
            }
        }
    }
}


static void testbdsvdunit_fillsparsede(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;


    ae_vector_set_length(d, n-1+1, _state);
    ae_vector_set_length(e, ae_maxint(0, n-2, _state)+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
        {
            d->ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        else
        {
            d->ptr.p_double[i] = 0;
        }
    }
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
        {
            e->ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        else
        {
            e->ptr.p_double[i] = 0;
        }
    }
}


static void testbdsvdunit_getbdsvderror(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     ae_bool isupper,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_matrix* c,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double locerr;
    double sm;


    
    /*
     * decomposition error
     */
    locerr = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            sm = 0;
            for(k=0; k<=n-1; k++)
            {
                sm = sm+w->ptr.p_double[k]*u->ptr.pp_double[i][k]*vt->ptr.pp_double[k][j];
            }
            if( isupper )
            {
                if( i==j )
                {
                    locerr = ae_maxreal(locerr, ae_fabs(d->ptr.p_double[i]-sm, _state), _state);
                }
                else
                {
                    if( i==j-1 )
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(e->ptr.p_double[i]-sm, _state), _state);
                    }
                    else
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
                    }
                }
            }
            else
            {
                if( i==j )
                {
                    locerr = ae_maxreal(locerr, ae_fabs(d->ptr.p_double[i]-sm, _state), _state);
                }
                else
                {
                    if( i-1==j )
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(e->ptr.p_double[j]-sm, _state), _state);
                    }
                    else
                    {
                        locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
                    }
                }
            }
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * check for C = U'
     * we consider it as decomposition error
     */
    locerr = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            locerr = ae_maxreal(locerr, ae_fabs(u->ptr.pp_double[i][j]-c->ptr.pp_double[j][i], _state), _state);
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * orthogonality error
     */
    locerr = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=i; j<=n-1; j++)
        {
            sm = ae_v_dotproduct(&u->ptr.pp_double[0][i], u->stride, &u->ptr.pp_double[0][j], u->stride, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
            sm = ae_v_dotproduct(&vt->ptr.pp_double[i][0], 1, &vt->ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
        }
    }
    *orterr = ae_maxreal(*orterr, locerr, _state);
    
    /*
     * values order error
     */
    for(i=1; i<=n-1; i++)
    {
        if( ae_fp_greater(w->ptr.p_double[i],w->ptr.p_double[i-1]) )
        {
            *wsorted = ae_false;
        }
    }
}


static void testbdsvdunit_testbdsvdproblem(/* Real    */ ae_vector* d,
     /* Real    */ ae_vector* e,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix u;
    ae_matrix vt;
    ae_matrix c;
    ae_vector w;
    ae_int_t i;
    double mx;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    mx = 0;
    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_greater(ae_fabs(d->ptr.p_double[i], _state),mx) )
        {
            mx = ae_fabs(d->ptr.p_double[i], _state);
        }
    }
    for(i=0; i<=n-2; i++)
    {
        if( ae_fp_greater(ae_fabs(e->ptr.p_double[i], _state),mx) )
        {
            mx = ae_fabs(e->ptr.p_double[i], _state);
        }
    }
    if( ae_fp_eq(mx,0) )
    {
        mx = 1;
    }
    
    /*
     * Upper BDSVD tests
     */
    ae_vector_set_length(&w, n-1+1, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_true, ae_false, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_true, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_true, ae_true, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_true, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    
    /*
     * Lower BDSVD tests
     */
    ae_vector_set_length(&w, n-1+1, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_false, ae_false, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_false, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    testbdsvdunit_fillidentity(&u, n, _state);
    testbdsvdunit_fillidentity(&vt, n, _state);
    testbdsvdunit_fillidentity(&c, n, _state);
    for(i=0; i<=n-1; i++)
    {
        w.ptr.p_double[i] = d->ptr.p_double[i];
    }
    if( !rmatrixbdsvd(&w, e, n, ae_false, ae_true, &u, n, &c, n, &vt, n, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testbdsvdunit_getbdsvderror(d, e, n, ae_false, &u, &c, &w, &vt, materr, orterr, wsorted, _state);
    
    /*
     * update counter
     */
    *succcount = *succcount+1;
    ae_frame_leave(_state);
}



static void testsvdunit_fillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testsvdunit_getsvderror(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state);
static void testsvdunit_testsvdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double* materr,
     double* orterr,
     double* othererr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state);





/*************************************************************************
Testing SVD decomposition subroutine
*************************************************************************/
ae_bool testsvd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_int_t m;
    ae_int_t n;
    ae_int_t maxmn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t gpass;
    ae_int_t pass;
    ae_bool waserrors;
    ae_bool wsorted;
    ae_bool wfailed;
    double materr;
    double orterr;
    double othererr;
    double threshold;
    double failthreshold;
    double failr;
    ae_int_t failcount;
    ae_int_t succcount;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    failcount = 0;
    succcount = 0;
    materr = 0;
    orterr = 0;
    othererr = 0;
    wsorted = ae_true;
    wfailed = ae_false;
    waserrors = ae_false;
    maxmn = 30;
    threshold = 5*100*ae_machineepsilon;
    failthreshold = 5.0E-3;
    ae_matrix_set_length(&a, maxmn-1+1, maxmn-1+1, _state);
    
    /*
     * TODO: div by zero fail, convergence fail
     */
    for(gpass=1; gpass<=1; gpass++)
    {
        
        /*
         * zero matrix, several cases
         */
        for(i=0; i<=maxmn-1; i++)
        {
            for(j=0; j<=maxmn-1; j++)
            {
                a.ptr.pp_double[i][j] = 0;
            }
        }
        for(i=1; i<=ae_minint(5, maxmn, _state); i++)
        {
            for(j=1; j<=ae_minint(5, maxmn, _state); j++)
            {
                testsvdunit_testsvdproblem(&a, i, j, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        
        /*
         * Long dense matrix
         */
        for(i=0; i<=maxmn-1; i++)
        {
            for(j=0; j<=ae_minint(5, maxmn, _state)-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        for(i=1; i<=maxmn; i++)
        {
            for(j=1; j<=ae_minint(5, maxmn, _state); j++)
            {
                testsvdunit_testsvdproblem(&a, i, j, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        for(i=0; i<=ae_minint(5, maxmn, _state)-1; i++)
        {
            for(j=0; j<=maxmn-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        for(i=1; i<=ae_minint(5, maxmn, _state); i++)
        {
            for(j=1; j<=maxmn; j++)
            {
                testsvdunit_testsvdproblem(&a, i, j, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        
        /*
         * Dense matrices
         */
        for(m=1; m<=ae_minint(10, maxmn, _state); m++)
        {
            for(n=1; n<=ae_minint(10, maxmn, _state); n++)
            {
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                }
                testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
            }
        }
        
        /*
         * Sparse matrices, very sparse matrices, incredible sparse matrices
         */
        for(m=1; m<=10; m++)
        {
            for(n=1; n<=10; n++)
            {
                for(pass=1; pass<=2; pass++)
                {
                    testsvdunit_fillsparsea(&a, m, n, 0.8, _state);
                    testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
                    testsvdunit_fillsparsea(&a, m, n, 0.9, _state);
                    testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
                    testsvdunit_fillsparsea(&a, m, n, 0.95, _state);
                    testsvdunit_testsvdproblem(&a, m, n, &materr, &orterr, &othererr, &wsorted, &wfailed, &failcount, &succcount, _state);
                }
            }
        }
    }
    
    /*
     * report
     */
    failr = (double)failcount/(double)(succcount+failcount);
    waserrors = (((ae_fp_greater(materr,threshold)||ae_fp_greater(orterr,threshold))||ae_fp_greater(othererr,threshold))||!wsorted)||ae_fp_greater(failr,failthreshold);
    if( !silent )
    {
        printf("TESTING SVD DECOMPOSITION\n");
        printf("SVD decomposition error:                 %5.3e\n",
            (double)(materr));
        printf("SVD orthogonality error:                 %5.3e\n",
            (double)(orterr));
        printf("SVD with different parameters error:     %5.3e\n",
            (double)(othererr));
        printf("Singular values order:                   ");
        if( wsorted )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
            printf("Fail ratio:                              %5.3f\n",
                (double)(failr));
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testsvdunit_fillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_double[i][j] = 0;
            }
        }
    }
}


static void testsvdunit_getsvderror(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* u,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* vt,
     double* materr,
     double* orterr,
     ae_bool* wsorted,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t minmn;
    double locerr;
    double sm;


    minmn = ae_minint(m, n, _state);
    
    /*
     * decomposition error
     */
    locerr = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            sm = 0;
            for(k=0; k<=minmn-1; k++)
            {
                sm = sm+w->ptr.p_double[k]*u->ptr.pp_double[i][k]*vt->ptr.pp_double[k][j];
            }
            locerr = ae_maxreal(locerr, ae_fabs(a->ptr.pp_double[i][j]-sm, _state), _state);
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * orthogonality error
     */
    locerr = 0;
    for(i=0; i<=minmn-1; i++)
    {
        for(j=i; j<=minmn-1; j++)
        {
            sm = ae_v_dotproduct(&u->ptr.pp_double[0][i], u->stride, &u->ptr.pp_double[0][j], u->stride, ae_v_len(0,m-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
            sm = ae_v_dotproduct(&vt->ptr.pp_double[i][0], 1, &vt->ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(sm-1, _state), _state);
            }
        }
    }
    *orterr = ae_maxreal(*orterr, locerr, _state);
    
    /*
     * values order error
     */
    for(i=1; i<=minmn-1; i++)
    {
        if( ae_fp_greater(w->ptr.p_double[i],w->ptr.p_double[i-1]) )
        {
            *wsorted = ae_false;
        }
    }
}


static void testsvdunit_testsvdproblem(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     double* materr,
     double* orterr,
     double* othererr,
     ae_bool* wsorted,
     ae_bool* wfailed,
     ae_int_t* failcount,
     ae_int_t* succcount,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix u;
    ae_matrix vt;
    ae_matrix u2;
    ae_matrix vt2;
    ae_vector w;
    ae_vector w2;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ujob;
    ae_int_t vtjob;
    ae_int_t memjob;
    ae_int_t ucheck;
    ae_int_t vtcheck;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Main SVD test
     */
    if( !rmatrixsvd(a, m, n, 2, 2, 2, &w, &u, &vt, _state) )
    {
        *failcount = *failcount+1;
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    testsvdunit_getsvderror(a, m, n, &u, &w, &vt, materr, orterr, wsorted, _state);
    
    /*
     * Additional SVD tests
     */
    for(ujob=0; ujob<=2; ujob++)
    {
        for(vtjob=0; vtjob<=2; vtjob++)
        {
            for(memjob=0; memjob<=2; memjob++)
            {
                if( !rmatrixsvd(a, m, n, ujob, vtjob, memjob, &w2, &u2, &vt2, _state) )
                {
                    *failcount = *failcount+1;
                    *wfailed = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ucheck = 0;
                if( ujob==1 )
                {
                    ucheck = ae_minint(m, n, _state);
                }
                if( ujob==2 )
                {
                    ucheck = m;
                }
                vtcheck = 0;
                if( vtjob==1 )
                {
                    vtcheck = ae_minint(m, n, _state);
                }
                if( vtjob==2 )
                {
                    vtcheck = n;
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=ucheck-1; j++)
                    {
                        *othererr = ae_maxreal(*othererr, ae_fabs(u.ptr.pp_double[i][j]-u2.ptr.pp_double[i][j], _state), _state);
                    }
                }
                for(i=0; i<=vtcheck-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        *othererr = ae_maxreal(*othererr, ae_fabs(vt.ptr.pp_double[i][j]-vt2.ptr.pp_double[i][j], _state), _state);
                    }
                }
                for(i=0; i<=ae_minint(m, n, _state)-1; i++)
                {
                    *othererr = ae_maxreal(*othererr, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
                }
            }
        }
    }
    
    /*
     * update counter
     */
    *succcount = *succcount+1;
    ae_frame_leave(_state);
}



static void testlinregunit_generaterandomtask(double xl,
     double xr,
     ae_bool randomx,
     double ymin,
     double ymax,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state);
static void testlinregunit_generatetask(double a,
     double b,
     double xl,
     double xr,
     ae_bool randomx,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state);
static void testlinregunit_filltaskwithy(double a,
     double b,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state);
static double testlinregunit_generatenormal(double mean,
     double sigma,
     ae_state *_state);
static void testlinregunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state);
static void testlinregunit_unsetlr(linearmodel* lr, ae_state *_state);





ae_bool testlinreg(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double sigmathreshold;
    ae_int_t maxn;
    ae_int_t maxm;
    ae_int_t passcount;
    ae_int_t estpasscount;
    double threshold;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t tmpi;
    ae_int_t pass;
    ae_int_t epass;
    ae_int_t m;
    ae_int_t tasktype;
    ae_int_t modeltype;
    ae_int_t m1;
    ae_int_t m2;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t info;
    ae_int_t info2;
    ae_matrix xy;
    ae_matrix xy2;
    ae_vector s;
    ae_vector s2;
    ae_vector w2;
    ae_vector x;
    ae_vector ta;
    ae_vector tb;
    ae_vector tc;
    ae_vector xy0;
    ae_vector tmpweights;
    linearmodel w;
    linearmodel wt;
    linearmodel wt2;
    ae_vector x1;
    ae_vector x2;
    double y1;
    double y2;
    ae_bool allsame;
    double ea;
    double eb;
    double varatested;
    double varbtested;
    double a;
    double b;
    double vara;
    double varb;
    double a2;
    double b2;
    double covab;
    double corrab;
    double p;
    ae_int_t qcnt;
    ae_vector qtbl;
    ae_vector qvals;
    ae_vector qsigma;
    lrreport ar;
    lrreport ar2;
    double f;
    double fp;
    double fm;
    double v;
    double vv;
    double cvrmserror;
    double cvavgerror;
    double cvavgrelerror;
    double rmserror;
    double avgerror;
    double avgrelerror;
    ae_bool nondefect;
    double sinshift;
    double tasklevel;
    double noiselevel;
    double hstep;
    double sigma;
    double mean;
    double means;
    double stddev;
    double stddevs;
    ae_bool slcerrors;
    ae_bool slerrors;
    ae_bool grcoverrors;
    ae_bool gropterrors;
    ae_bool gresterrors;
    ae_bool grothererrors;
    ae_bool grconverrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ta, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xy0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpweights, 0, DT_REAL, _state, ae_true);
    _linearmodel_init(&w, _state, ae_true);
    _linearmodel_init(&wt, _state, ae_true);
    _linearmodel_init(&wt2, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qtbl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qvals, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&qsigma, 0, DT_REAL, _state, ae_true);
    _lrreport_init(&ar, _state, ae_true);
    _lrreport_init(&ar2, _state, ae_true);

    
    /*
     * Primary settings
     */
    maxn = 40;
    maxm = 5;
    passcount = 3;
    estpasscount = 1000;
    sigmathreshold = 7;
    threshold = 1000000*ae_machineepsilon;
    slerrors = ae_false;
    slcerrors = ae_false;
    grcoverrors = ae_false;
    gropterrors = ae_false;
    gresterrors = ae_false;
    grothererrors = ae_false;
    grconverrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Quantiles table setup
     */
    qcnt = 5;
    ae_vector_set_length(&qtbl, qcnt-1+1, _state);
    ae_vector_set_length(&qvals, qcnt-1+1, _state);
    ae_vector_set_length(&qsigma, qcnt-1+1, _state);
    qtbl.ptr.p_double[0] = 0.5;
    qtbl.ptr.p_double[1] = 0.25;
    qtbl.ptr.p_double[2] = 0.10;
    qtbl.ptr.p_double[3] = 0.05;
    qtbl.ptr.p_double[4] = 0.025;
    for(i=0; i<=qcnt-1; i++)
    {
        qsigma.ptr.p_double[i] = ae_sqrt(qtbl.ptr.p_double[i]*(1-qtbl.ptr.p_double[i])/estpasscount, _state);
    }
    
    /*
     * Other setup
     */
    ae_vector_set_length(&ta, estpasscount-1+1, _state);
    ae_vector_set_length(&tb, estpasscount-1+1, _state);
    
    /*
     * Test straight line regression
     */
    for(n=2; n<=maxn; n++)
    {
        
        /*
         * Fail/pass test
         */
        testlinregunit_generaterandomtask(-1, 1, ae_false, -1, 1, 1, 2, n, &xy, &s, _state);
        lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=1;
        testlinregunit_generaterandomtask(1, 1, ae_false, -1, 1, 1, 2, n, &xy, &s, _state);
        lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=-3;
        testlinregunit_generaterandomtask(-1, 1, ae_false, -1, 1, -1, -1, n, &xy, &s, _state);
        lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=-2;
        testlinregunit_generaterandomtask(-1, 1, ae_false, -1, 1, 2, 1, 2, &xy, &s, _state);
        lrlines(&xy, &s, 1, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        slcerrors = slcerrors||info!=-1;
        
        /*
         * Multipass tests
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Test S variant against non-S variant
             */
            ea = 2*ae_randomreal(_state)-1;
            eb = 2*ae_randomreal(_state)-1;
            testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), ae_fp_greater(ae_randomreal(_state),0.5), 1, 1, n, &xy, &s, _state);
            lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
            lrline(&xy, n, &info2, &a2, &b2, _state);
            if( info!=1||info2!=1 )
            {
                slcerrors = ae_true;
            }
            else
            {
                slerrors = (slerrors||ae_fp_greater(ae_fabs(a-a2, _state),threshold))||ae_fp_greater(ae_fabs(b-b2, _state),threshold);
            }
            
            /*
             * Test for A/B
             *
             * Generate task with exact, non-perturbed y[i],
             * then make non-zero s[i]
             */
            ea = 2*ae_randomreal(_state)-1;
            eb = 2*ae_randomreal(_state)-1;
            testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), n>4, 0.0, 0.0, n, &xy, &s, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = 1+ae_randomreal(_state);
            }
            lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
            if( info!=1 )
            {
                slcerrors = ae_true;
            }
            else
            {
                slerrors = (slerrors||ae_fp_greater(ae_fabs(a-ea, _state),0.001))||ae_fp_greater(ae_fabs(b-eb, _state),0.001);
            }
            
            /*
             * Test for VarA, VarB, P (P is being tested only for N>2)
             */
            for(i=0; i<=qcnt-1; i++)
            {
                qvals.ptr.p_double[i] = 0;
            }
            ea = 2*ae_randomreal(_state)-1;
            eb = 2*ae_randomreal(_state)-1;
            testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), n>4, 1.0, 2.0, n, &xy, &s, _state);
            lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
            if( info!=1 )
            {
                slcerrors = ae_true;
                continue;
            }
            varatested = vara;
            varbtested = varb;
            for(epass=0; epass<=estpasscount-1; epass++)
            {
                
                /*
                 * Generate
                 */
                testlinregunit_filltaskwithy(ea, eb, n, &xy, &s, _state);
                lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
                if( info!=1 )
                {
                    slcerrors = ae_true;
                    continue;
                }
                
                /*
                 * A, B, P
                 * (P is being tested for uniformity, additional p-tests are below)
                 */
                ta.ptr.p_double[epass] = a;
                tb.ptr.p_double[epass] = b;
                for(i=0; i<=qcnt-1; i++)
                {
                    if( ae_fp_less_eq(p,qtbl.ptr.p_double[i]) )
                    {
                        qvals.ptr.p_double[i] = qvals.ptr.p_double[i]+(double)1/(double)estpasscount;
                    }
                }
            }
            testlinregunit_calculatemv(&ta, estpasscount, &mean, &means, &stddev, &stddevs, _state);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(mean-ea, _state)/means,sigmathreshold);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(stddev-ae_sqrt(varatested, _state), _state)/stddevs,sigmathreshold);
            testlinregunit_calculatemv(&tb, estpasscount, &mean, &means, &stddev, &stddevs, _state);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(mean-eb, _state)/means,sigmathreshold);
            slerrors = slerrors||ae_fp_greater_eq(ae_fabs(stddev-ae_sqrt(varbtested, _state), _state)/stddevs,sigmathreshold);
            if( n>2 )
            {
                for(i=0; i<=qcnt-1; i++)
                {
                    if( ae_fp_greater(ae_fabs(qtbl.ptr.p_double[i]-qvals.ptr.p_double[i], _state)/qsigma.ptr.p_double[i],sigmathreshold) )
                    {
                        slerrors = ae_true;
                    }
                }
            }
            
            /*
             * Additional tests for P: correlation with fit quality
             */
            if( n>2 )
            {
                testlinregunit_generatetask(ea, eb, -5*ae_randomreal(_state), 5*ae_randomreal(_state), ae_false, 0.0, 0.0, n, &xy, &s, _state);
                for(i=0; i<=n-1; i++)
                {
                    s.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
                if( info!=1 )
                {
                    slcerrors = ae_true;
                    continue;
                }
                slerrors = slerrors||ae_fp_less(p,0.999);
                testlinregunit_generatetask(0, 0, -5*ae_randomreal(_state), 5*ae_randomreal(_state), ae_false, 1.0, 1.0, n, &xy, &s, _state);
                for(i=0; i<=n-1; i++)
                {
                    if( i%2==0 )
                    {
                        xy.ptr.pp_double[i][1] = 5.0;
                    }
                    else
                    {
                        xy.ptr.pp_double[i][1] = -5.0;
                    }
                }
                if( n%2!=0 )
                {
                    xy.ptr.pp_double[n-1][1] = 0;
                }
                lrlines(&xy, &s, n, &info, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
                if( info!=1 )
                {
                    slcerrors = ae_true;
                    continue;
                }
                slerrors = slerrors||ae_fp_greater(p,0.001);
            }
        }
    }
    
    /*
     * General regression tests:
     */
    
    /*
     * Simple linear tests (small sample, optimum point, covariance)
     */
    for(n=3; n<=maxn; n++)
    {
        ae_vector_set_length(&s, n-1+1, _state);
        
        /*
         * Linear tests:
         * a. random points, sigmas
         * b. no sigmas
         */
        ae_matrix_set_length(&xy, n-1+1, 1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
            xy.ptr.pp_double[i][1] = 2*ae_randomreal(_state)-1;
            s.ptr.p_double[i] = 1+ae_randomreal(_state);
        }
        lrbuilds(&xy, &s, n, 1, &info, &wt, &ar, _state);
        if( info!=1 )
        {
            grconverrors = ae_true;
            continue;
        }
        lrunpack(&wt, &tmpweights, &tmpi, _state);
        lrlines(&xy, &s, n, &info2, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(a-tmpweights.ptr.p_double[1], _state),threshold);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(b-tmpweights.ptr.p_double[0], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(vara-ar.c.ptr.pp_double[1][1], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(varb-ar.c.ptr.pp_double[0][0], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(covab-ar.c.ptr.pp_double[1][0], _state),threshold);
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(covab-ar.c.ptr.pp_double[0][1], _state),threshold);
        lrbuild(&xy, n, 1, &info, &wt, &ar, _state);
        if( info!=1 )
        {
            grconverrors = ae_true;
            continue;
        }
        lrunpack(&wt, &tmpweights, &tmpi, _state);
        lrline(&xy, n, &info2, &a, &b, _state);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(a-tmpweights.ptr.p_double[1], _state),threshold);
        gropterrors = gropterrors||ae_fp_greater(ae_fabs(b-tmpweights.ptr.p_double[0], _state),threshold);
    }
    
    /*
     * S covariance versus S-less covariance.
     * Slightly skewed task, large sample size.
     * Will S-less subroutine estimate covariance matrix good enough?
     */
    n = 1000+ae_randominteger(3000, _state);
    sigma = 0.1+ae_randomreal(_state)*1.9;
    ae_matrix_set_length(&xy, n-1+1, 1+1, _state);
    ae_vector_set_length(&s, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        xy.ptr.pp_double[i][0] = 1.5*ae_randomreal(_state)-0.5;
        xy.ptr.pp_double[i][1] = 1.2*xy.ptr.pp_double[i][0]-0.3+testlinregunit_generatenormal(0, sigma, _state);
        s.ptr.p_double[i] = sigma;
    }
    lrbuild(&xy, n, 1, &info, &wt, &ar, _state);
    lrlines(&xy, &s, n, &info2, &a, &b, &vara, &varb, &covab, &corrab, &p, _state);
    if( info!=1||info2!=1 )
    {
        grconverrors = ae_true;
    }
    else
    {
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[0][0]/varb, _state), _state),ae_log(1.2, _state));
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[1][1]/vara, _state), _state),ae_log(1.2, _state));
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[0][1]/covab, _state), _state),ae_log(1.2, _state));
        grcoverrors = grcoverrors||ae_fp_greater(ae_fabs(ae_log(ar.c.ptr.pp_double[1][0]/covab, _state), _state),ae_log(1.2, _state));
    }
    
    /*
     * General tests:
     * * basis functions - up to cubic
     * * task types:
     * * data set is noisy sine half-period with random shift
     * * tests:
     *   unpacking/packing
     *   optimality
     *   error estimates
     * * tasks:
     *   0 = noised sine
     *   1 = degenerate task with 1-of-n encoded categorical variables
     *   2 = random task with large variation (for 1-type models)
     *   3 = random task with small variation (for 1-type models)
     *
     *   Additional tasks TODO
     *   specially designed task with defective vectors which leads to
     *   the failure of the fast CV formula.
     *
     */
    for(modeltype=0; modeltype<=1; modeltype++)
    {
        for(tasktype=0; tasktype<=3; tasktype++)
        {
            if( tasktype==0 )
            {
                m1 = 1;
                m2 = 3;
            }
            if( tasktype==1 )
            {
                m1 = 9;
                m2 = 9;
            }
            if( tasktype==2||tasktype==3 )
            {
                m1 = 9;
                m2 = 9;
            }
            for(m=m1; m<=m2; m++)
            {
                if( tasktype==0 )
                {
                    n1 = m+3;
                    n2 = m+20;
                }
                if( tasktype==1 )
                {
                    n1 = 70+ae_randominteger(70, _state);
                    n2 = n1;
                }
                if( tasktype==2||tasktype==3 )
                {
                    n1 = 100;
                    n2 = n1;
                }
                for(n=n1; n<=n2; n++)
                {
                    ae_matrix_set_length(&xy, n-1+1, m+1, _state);
                    ae_vector_set_length(&xy0, n-1+1, _state);
                    ae_vector_set_length(&s, n-1+1, _state);
                    hstep = 0.001;
                    noiselevel = 0.2;
                    
                    /*
                     * Prepare task
                     */
                    if( tasktype==0 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=1; j<=m-1; j++)
                            {
                                xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][0]*xy.ptr.pp_double[i][j-1];
                            }
                        }
                        sinshift = ae_randomreal(_state)*ae_pi;
                        for(i=0; i<=n-1; i++)
                        {
                            xy0.ptr.p_double[i] = ae_sin(sinshift+ae_pi*0.5*(xy.ptr.pp_double[i][0]+1), _state);
                            xy.ptr.pp_double[i][m] = xy0.ptr.p_double[i]+noiselevel*testlinregunit_generatenormal(0, 1, _state);
                        }
                    }
                    if( tasktype==1 )
                    {
                        ae_assert(m==9, "Assertion failed", _state);
                        ae_vector_set_length(&ta, 8+1, _state);
                        ta.ptr.p_double[0] = 1;
                        ta.ptr.p_double[1] = 2;
                        ta.ptr.p_double[2] = 3;
                        ta.ptr.p_double[3] = 0.25;
                        ta.ptr.p_double[4] = 0.5;
                        ta.ptr.p_double[5] = 0.75;
                        ta.ptr.p_double[6] = 0.06;
                        ta.ptr.p_double[7] = 0.12;
                        ta.ptr.p_double[8] = 0.18;
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                xy.ptr.pp_double[i][j] = 0;
                            }
                            xy.ptr.pp_double[i][0+i%3] = 1;
                            xy.ptr.pp_double[i][3+i/3%3] = 1;
                            xy.ptr.pp_double[i][6+i/9%3] = 1;
                            v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &ta.ptr.p_double[0], 1, ae_v_len(0,8));
                            xy0.ptr.p_double[i] = v;
                            xy.ptr.pp_double[i][m] = v+noiselevel*testlinregunit_generatenormal(0, 1, _state);
                        }
                    }
                    if( tasktype==2||tasktype==3 )
                    {
                        ae_assert(m==9, "Assertion failed", _state);
                        ae_vector_set_length(&ta, 8+1, _state);
                        ta.ptr.p_double[0] = 1;
                        ta.ptr.p_double[1] = -2;
                        ta.ptr.p_double[2] = 3;
                        ta.ptr.p_double[3] = 0.25;
                        ta.ptr.p_double[4] = -0.5;
                        ta.ptr.p_double[5] = 0.75;
                        ta.ptr.p_double[6] = -0.06;
                        ta.ptr.p_double[7] = 0.12;
                        ta.ptr.p_double[8] = -0.18;
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=m-1; j++)
                            {
                                if( tasktype==2 )
                                {
                                    xy.ptr.pp_double[i][j] = 1+testlinregunit_generatenormal(0, 3, _state);
                                }
                                else
                                {
                                    xy.ptr.pp_double[i][j] = 1+testlinregunit_generatenormal(0, 0.05, _state);
                                }
                            }
                            v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &ta.ptr.p_double[0], 1, ae_v_len(0,8));
                            xy0.ptr.p_double[i] = v;
                            xy.ptr.pp_double[i][m] = v+noiselevel*testlinregunit_generatenormal(0, 1, _state);
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        s.ptr.p_double[i] = 1+ae_randomreal(_state);
                    }
                    
                    /*
                     * Solve (using S-variant, non-S-variant is not tested)
                     */
                    if( modeltype==0 )
                    {
                        lrbuilds(&xy, &s, n, m, &info, &wt, &ar, _state);
                    }
                    else
                    {
                        lrbuildzs(&xy, &s, n, m, &info, &wt, &ar, _state);
                    }
                    if( info!=1 )
                    {
                        grconverrors = ae_true;
                        continue;
                    }
                    lrunpack(&wt, &tmpweights, &tmpi, _state);
                    
                    /*
                     * LRProcess test
                     */
                    ae_vector_set_length(&x, m-1+1, _state);
                    v = tmpweights.ptr.p_double[m];
                    for(i=0; i<=m-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        v = v+tmpweights.ptr.p_double[i]*x.ptr.p_double[i];
                    }
                    grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-lrprocess(&wt, &x, _state), _state)/ae_maxreal(ae_fabs(v, _state), 1, _state),threshold);
                    
                    /*
                     * LRPack test
                     */
                    lrpack(&tmpweights, m, &wt2, _state);
                    ae_vector_set_length(&x, m-1+1, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    v = lrprocess(&wt, &x, _state);
                    grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-lrprocess(&wt2, &x, _state), _state)/ae_fabs(v, _state),threshold);
                    
                    /*
                     * Optimality test
                     */
                    for(k=0; k<=m; k++)
                    {
                        if( modeltype==1&&k==m )
                        {
                            
                            /*
                             * 0-type models (with non-zero constant term)
                             * are tested for optimality of all coefficients.
                             *
                             * 1-type models (with zero constant term)
                             * are tested for optimality of non-constant terms only.
                             */
                            continue;
                        }
                        f = 0;
                        fp = 0;
                        fm = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = tmpweights.ptr.p_double[m];
                            for(j=0; j<=m-1; j++)
                            {
                                v = v+xy.ptr.pp_double[i][j]*tmpweights.ptr.p_double[j];
                            }
                            f = f+ae_sqr((v-xy.ptr.pp_double[i][m])/s.ptr.p_double[i], _state);
                            if( k<m )
                            {
                                vv = xy.ptr.pp_double[i][k];
                            }
                            else
                            {
                                vv = 1;
                            }
                            fp = fp+ae_sqr((v+vv*hstep-xy.ptr.pp_double[i][m])/s.ptr.p_double[i], _state);
                            fm = fm+ae_sqr((v-vv*hstep-xy.ptr.pp_double[i][m])/s.ptr.p_double[i], _state);
                        }
                        gropterrors = (gropterrors||ae_fp_greater(f,fp))||ae_fp_greater(f,fm);
                    }
                    
                    /*
                     * Covariance matrix test:
                     * generate random vector, project coefficients on it,
                     * compare variance of projection with estimate provided
                     * by cov.matrix
                     */
                    ae_vector_set_length(&ta, estpasscount-1+1, _state);
                    ae_vector_set_length(&tb, m+1, _state);
                    ae_vector_set_length(&tc, m+1, _state);
                    ae_matrix_set_length(&xy2, n-1+1, m+1, _state);
                    for(i=0; i<=m; i++)
                    {
                        tb.ptr.p_double[i] = testlinregunit_generatenormal(0, 1, _state);
                    }
                    for(epass=0; epass<=estpasscount-1; epass++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            ae_v_move(&xy2.ptr.pp_double[i][0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
                            xy2.ptr.pp_double[i][m] = xy0.ptr.p_double[i]+s.ptr.p_double[i]*testlinregunit_generatenormal(0, 1, _state);
                        }
                        if( modeltype==0 )
                        {
                            lrbuilds(&xy2, &s, n, m, &info, &wt, &ar2, _state);
                        }
                        else
                        {
                            lrbuildzs(&xy2, &s, n, m, &info, &wt, &ar2, _state);
                        }
                        if( info!=1 )
                        {
                            ta.ptr.p_double[epass] = 0;
                            grconverrors = ae_true;
                            continue;
                        }
                        lrunpack(&wt, &w2, &tmpi, _state);
                        v = ae_v_dotproduct(&tb.ptr.p_double[0], 1, &w2.ptr.p_double[0], 1, ae_v_len(0,m));
                        ta.ptr.p_double[epass] = v;
                    }
                    testlinregunit_calculatemv(&ta, estpasscount, &mean, &means, &stddev, &stddevs, _state);
                    for(i=0; i<=m; i++)
                    {
                        v = ae_v_dotproduct(&tb.ptr.p_double[0], 1, &ar.c.ptr.pp_double[0][i], ar.c.stride, ae_v_len(0,m));
                        tc.ptr.p_double[i] = v;
                    }
                    v = ae_v_dotproduct(&tc.ptr.p_double[0], 1, &tb.ptr.p_double[0], 1, ae_v_len(0,m));
                    grcoverrors = grcoverrors||ae_fp_greater_eq(ae_fabs((ae_sqrt(v, _state)-stddev)/stddevs, _state),sigmathreshold);
                    
                    /*
                     * Test for the fast CV error:
                     * calculate CV error by definition (leaving out N
                     * points and recalculating solution).
                     *
                     * Test for the training set error
                     */
                    cvrmserror = 0;
                    cvavgerror = 0;
                    cvavgrelerror = 0;
                    rmserror = 0;
                    avgerror = 0;
                    avgrelerror = 0;
                    ae_matrix_set_length(&xy2, n-2+1, m+1, _state);
                    ae_vector_set_length(&s2, n-2+1, _state);
                    for(i=0; i<=n-2; i++)
                    {
                        ae_v_move(&xy2.ptr.pp_double[i][0], 1, &xy.ptr.pp_double[i+1][0], 1, ae_v_len(0,m));
                        s2.ptr.p_double[i] = s.ptr.p_double[i+1];
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        
                        /*
                         * Trn
                         */
                        v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &tmpweights.ptr.p_double[0], 1, ae_v_len(0,m-1));
                        v = v+tmpweights.ptr.p_double[m];
                        rmserror = rmserror+ae_sqr(v-xy.ptr.pp_double[i][m], _state);
                        avgerror = avgerror+ae_fabs(v-xy.ptr.pp_double[i][m], _state);
                        avgrelerror = avgrelerror+ae_fabs((v-xy.ptr.pp_double[i][m])/xy.ptr.pp_double[i][m], _state);
                        
                        /*
                         * CV: non-defect vectors only
                         */
                        nondefect = ae_true;
                        for(k=0; k<=ar.ncvdefects-1; k++)
                        {
                            if( ar.cvdefects.ptr.p_int[k]==i )
                            {
                                nondefect = ae_false;
                            }
                        }
                        if( nondefect )
                        {
                            if( modeltype==0 )
                            {
                                lrbuilds(&xy2, &s2, n-1, m, &info2, &wt, &ar2, _state);
                            }
                            else
                            {
                                lrbuildzs(&xy2, &s2, n-1, m, &info2, &wt, &ar2, _state);
                            }
                            if( info2!=1 )
                            {
                                grconverrors = ae_true;
                                continue;
                            }
                            lrunpack(&wt, &w2, &tmpi, _state);
                            v = ae_v_dotproduct(&xy.ptr.pp_double[i][0], 1, &w2.ptr.p_double[0], 1, ae_v_len(0,m-1));
                            v = v+w2.ptr.p_double[m];
                            cvrmserror = cvrmserror+ae_sqr(v-xy.ptr.pp_double[i][m], _state);
                            cvavgerror = cvavgerror+ae_fabs(v-xy.ptr.pp_double[i][m], _state);
                            cvavgrelerror = cvavgrelerror+ae_fabs((v-xy.ptr.pp_double[i][m])/xy.ptr.pp_double[i][m], _state);
                        }
                        
                        /*
                         * Next set
                         */
                        if( i!=n-1 )
                        {
                            ae_v_move(&xy2.ptr.pp_double[i][0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,m));
                            s2.ptr.p_double[i] = s.ptr.p_double[i];
                        }
                    }
                    cvrmserror = ae_sqrt(cvrmserror/(n-ar.ncvdefects), _state);
                    cvavgerror = cvavgerror/(n-ar.ncvdefects);
                    cvavgrelerror = cvavgrelerror/(n-ar.ncvdefects);
                    rmserror = ae_sqrt(rmserror/n, _state);
                    avgerror = avgerror/n;
                    avgrelerror = avgrelerror/n;
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.cvrmserror/cvrmserror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.cvavgerror/cvavgerror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.cvavgrelerror/cvavgrelerror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.rmserror/rmserror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.avgerror/avgerror, _state), _state),ae_log(1+1.0E-5, _state));
                    gresterrors = gresterrors||ae_fp_greater(ae_fabs(ae_log(ar.avgrelerror/avgrelerror, _state), _state),ae_log(1+1.0E-5, _state));
                }
            }
        }
    }
    
    /*
     * Additional subroutines
     */
    for(pass=1; pass<=50; pass++)
    {
        n = 2;
        do
        {
            noiselevel = ae_randomreal(_state)+0.1;
            tasklevel = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_fabs(noiselevel-tasklevel, _state),0.05));
        ae_matrix_set_length(&xy, 3*n-1+1, 1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[3*i+0][0] = i;
            xy.ptr.pp_double[3*i+1][0] = i;
            xy.ptr.pp_double[3*i+2][0] = i;
            xy.ptr.pp_double[3*i+0][1] = tasklevel-noiselevel;
            xy.ptr.pp_double[3*i+1][1] = tasklevel;
            xy.ptr.pp_double[3*i+2][1] = tasklevel+noiselevel;
        }
        lrbuild(&xy, 3*n, 1, &info, &wt, &ar, _state);
        if( info==1 )
        {
            lrunpack(&wt, &tmpweights, &tmpi, _state);
            v = lrrmserror(&wt, &xy, 3*n, _state);
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-noiselevel*ae_sqrt((double)2/(double)3, _state), _state),threshold);
            v = lravgerror(&wt, &xy, 3*n, _state);
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-noiselevel*((double)2/(double)3), _state),threshold);
            v = lravgrelerror(&wt, &xy, 3*n, _state);
            vv = (ae_fabs(noiselevel/(tasklevel-noiselevel), _state)+ae_fabs(noiselevel/(tasklevel+noiselevel), _state))/3;
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-vv, _state),threshold*vv);
        }
        else
        {
            grothererrors = ae_true;
        }
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[3*i+0][0] = i;
            xy.ptr.pp_double[3*i+1][0] = i;
            xy.ptr.pp_double[3*i+2][0] = i;
            xy.ptr.pp_double[3*i+0][1] = -noiselevel;
            xy.ptr.pp_double[3*i+1][1] = 0;
            xy.ptr.pp_double[3*i+2][1] = noiselevel;
        }
        lrbuild(&xy, 3*n, 1, &info, &wt, &ar, _state);
        if( info==1 )
        {
            lrunpack(&wt, &tmpweights, &tmpi, _state);
            v = lravgrelerror(&wt, &xy, 3*n, _state);
            grothererrors = grothererrors||ae_fp_greater(ae_fabs(v-1, _state),threshold);
        }
        else
        {
            grothererrors = ae_true;
        }
    }
    for(pass=1; pass<=10; pass++)
    {
        m = 1+ae_randominteger(5, _state);
        n = 10+ae_randominteger(10, _state);
        ae_matrix_set_length(&xy, n-1+1, m+1, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m; j++)
            {
                xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
        }
        lrbuild(&xy, n, m, &info, &w, &ar, _state);
        if( info<0 )
        {
            grothererrors = ae_true;
            break;
        }
        ae_vector_set_length(&x1, m-1+1, _state);
        ae_vector_set_length(&x2, m-1+1, _state);
        
        /*
         * Same inputs on original leads to same outputs
         * on copy created using LRCopy
         */
        testlinregunit_unsetlr(&wt, _state);
        lrcopy(&w, &wt, _state);
        for(i=0; i<=m-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        y1 = lrprocess(&w, &x1, _state);
        y2 = lrprocess(&wt, &x2, _state);
        allsame = ae_fp_eq(y1,y2);
        grothererrors = grothererrors||!allsame;
    }
    
    /*
     * TODO: Degenerate tests (when design matrix and right part are zero)
     */
    
    /*
     * Final report
     */
    waserrors = (((((slerrors||slcerrors)||gropterrors)||grcoverrors)||gresterrors)||grothererrors)||grconverrors;
    if( !silent )
    {
        printf("REGRESSION TEST\n");
        printf("STRAIGHT LINE REGRESSION:                ");
        if( !slerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("STRAIGHT LINE REGRESSION CONVERGENCE:    ");
        if( !slcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("GENERAL LINEAR REGRESSION:               ");
        if( !((((gropterrors||grcoverrors)||gresterrors)||grothererrors)||grconverrors) )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OPTIMALITY:                            ");
        if( !gropterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* COV. MATRIX:                           ");
        if( !grcoverrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* ERROR ESTIMATES:                       ");
        if( !gresterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* CONVERGENCE:                           ");
        if( !grconverrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OTHER SUBROUTINES:                     ");
        if( !grothererrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Task generation. Meaningless task, just random numbers.
*************************************************************************/
static void testlinregunit_generaterandomtask(double xl,
     double xr,
     ae_bool randomx,
     double ymin,
     double ymax,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_matrix_set_length(xy, n-1+1, 1+1, _state);
    ae_vector_set_length(s, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( randomx )
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*ae_randomreal(_state);
        }
        else
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*i/(n-1);
        }
        xy->ptr.pp_double[i][1] = ymin+(ymax-ymin)*ae_randomreal(_state);
        s->ptr.p_double[i] = smin+(smax-smin)*ae_randomreal(_state);
    }
}


/*************************************************************************
Task generation.
*************************************************************************/
static void testlinregunit_generatetask(double a,
     double b,
     double xl,
     double xr,
     ae_bool randomx,
     double smin,
     double smax,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    ae_matrix_set_length(xy, n-1+1, 1+1, _state);
    ae_vector_set_length(s, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( randomx )
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*ae_randomreal(_state);
        }
        else
        {
            xy->ptr.pp_double[i][0] = xl+(xr-xl)*i/(n-1);
        }
        s->ptr.p_double[i] = smin+(smax-smin)*ae_randomreal(_state);
        xy->ptr.pp_double[i][1] = a+b*xy->ptr.pp_double[i][0]+testlinregunit_generatenormal(0, s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
Task generation.
y[i] are filled based on A, B, X[I], S[I]
*************************************************************************/
static void testlinregunit_filltaskwithy(double a,
     double b,
     ae_int_t n,
     /* Real    */ ae_matrix* xy,
     /* Real    */ ae_vector* s,
     ae_state *_state)
{
    ae_int_t i;


    for(i=0; i<=n-1; i++)
    {
        xy->ptr.pp_double[i][1] = a+b*xy->ptr.pp_double[i][0]+testlinregunit_generatenormal(0, s->ptr.p_double[i], _state);
    }
}


/*************************************************************************
Normal random numbers
*************************************************************************/
static double testlinregunit_generatenormal(double mean,
     double sigma,
     ae_state *_state)
{
    double u;
    double v;
    double sum;
    double result;


    result = mean;
    for(;;)
    {
        u = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        v = (2*ae_randominteger(2, _state)-1)*ae_randomreal(_state);
        sum = u*u+v*v;
        if( ae_fp_less(sum,1)&&ae_fp_greater(sum,0) )
        {
            sum = ae_sqrt(-2*ae_log(sum, _state)/sum, _state);
            result = sigma*u*sum+mean;
            return result;
        }
    }
    return result;
}


/*************************************************************************
Moments estimates and their errors
*************************************************************************/
static void testlinregunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state)
{
    ae_int_t i;
    double v1;
    double v2;
    double variance;

    *mean = 0;
    *means = 0;
    *stddev = 0;
    *stddevs = 0;

    *mean = 0;
    *means = 1;
    *stddev = 0;
    *stddevs = 1;
    variance = 0;
    if( n<=1 )
    {
        return;
    }
    
    /*
     * Mean
     */
    for(i=0; i<=n-1; i++)
    {
        *mean = *mean+x->ptr.p_double[i];
    }
    *mean = *mean/n;
    
    /*
     * Variance (using corrected two-pass algorithm)
     */
    if( n!=1 )
    {
        v1 = 0;
        for(i=0; i<=n-1; i++)
        {
            v1 = v1+ae_sqr(x->ptr.p_double[i]-(*mean), _state);
        }
        v2 = 0;
        for(i=0; i<=n-1; i++)
        {
            v2 = v2+(x->ptr.p_double[i]-(*mean));
        }
        v2 = ae_sqr(v2, _state)/n;
        variance = (v1-v2)/(n-1);
        if( ae_fp_less(variance,0) )
        {
            variance = 0;
        }
        *stddev = ae_sqrt(variance, _state);
    }
    
    /*
     * Errors
     */
    *means = *stddev/ae_sqrt(n, _state);
    *stddevs = *stddev*ae_sqrt(2, _state)/ae_sqrt(n-1, _state);
}


/*************************************************************************
Unsets LR
*************************************************************************/
static void testlinregunit_unsetlr(linearmodel* lr, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t info;
    lrreport rep;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _lrreport_init(&rep, _state, ae_true);

    ae_matrix_set_length(&xy, 5+1, 1+1, _state);
    for(i=0; i<=5; i++)
    {
        xy.ptr.pp_double[i][0] = 0;
        xy.ptr.pp_double[i][1] = 0;
    }
    lrbuild(&xy, 6, 1, &info, lr, &rep, _state);
    ae_assert(info>0, "Assertion failed", _state);
    ae_frame_leave(_state);
}








ae_bool testxblas(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool approxerrors;
    ae_bool exactnesserrors;
    ae_bool waserrors;
    double approxthreshold;
    ae_int_t maxn;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t i;
    ae_int_t pass;
    double rv1;
    double rv2;
    double rv2err;
    ae_complex cv1;
    ae_complex cv2;
    double cv2err;
    ae_vector rx;
    ae_vector ry;
    ae_vector cx;
    ae_vector cy;
    ae_vector temp;
    double s;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&rx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ry, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cx, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cy, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&temp, 0, DT_REAL, _state, ae_true);

    approxerrors = ae_false;
    exactnesserrors = ae_false;
    waserrors = ae_false;
    approxthreshold = 1000*ae_machineepsilon;
    maxn = 1000;
    passcount = 10;
    
    /*
     * tests:
     * 1. ability to calculate dot product
     * 2. higher precision
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             *  ability to approximately calculate real dot product
             */
            ae_vector_set_length(&rx, n, _state);
            ae_vector_set_length(&ry, n, _state);
            ae_vector_set_length(&temp, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    rx.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    rx.ptr.p_double[i] = 0;
                }
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    ry.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    ry.ptr.p_double[i] = 0;
                }
            }
            rv1 = ae_v_dotproduct(&rx.ptr.p_double[0], 1, &ry.ptr.p_double[0], 1, ae_v_len(0,n-1));
            xdot(&rx, &ry, n, &temp, &rv2, &rv2err, _state);
            approxerrors = approxerrors||ae_fp_greater(ae_fabs(rv1-rv2, _state),approxthreshold);
            
            /*
             *  ability to approximately calculate complex dot product
             */
            ae_vector_set_length(&cx, n, _state);
            ae_vector_set_length(&cy, n, _state);
            ae_vector_set_length(&temp, 2*n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    cx.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                    cx.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    cx.ptr.p_complex[i] = ae_complex_from_d(0);
                }
                if( ae_fp_greater(ae_randomreal(_state),0.2) )
                {
                    cy.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                    cy.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                }
                else
                {
                    cy.ptr.p_complex[i] = ae_complex_from_d(0);
                }
            }
            cv1 = ae_v_cdotproduct(&cx.ptr.p_complex[0], 1, "N", &cy.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
            xcdot(&cx, &cy, n, &temp, &cv2, &cv2err, _state);
            approxerrors = approxerrors||ae_fp_greater(ae_c_abs(ae_c_sub(cv1,cv2), _state),approxthreshold);
        }
    }
    
    /*
     * test of precision: real
     */
    n = 50000;
    ae_vector_set_length(&rx, n, _state);
    ae_vector_set_length(&ry, n, _state);
    ae_vector_set_length(&temp, n, _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        ae_assert(n%2==0, "Assertion failed", _state);
        
        /*
         * First test: X + X + ... + X - X - X - ... - X = 1*X
         */
        s = ae_exp(ae_maxint(pass, 50, _state), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        ry.ptr.p_double[0] = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        for(i=1; i<=n-1; i++)
        {
            ry.ptr.p_double[i] = ry.ptr.p_double[0];
        }
        for(i=0; i<=n/2-1; i++)
        {
            rx.ptr.p_double[i] = 1;
        }
        for(i=n/2; i<=n-2; i++)
        {
            rx.ptr.p_double[i] = -1;
        }
        rx.ptr.p_double[n-1] = 0;
        xdot(&rx, &ry, n, &temp, &rv2, &rv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(rv2err,0);
        exactnesserrors = exactnesserrors||ae_fp_greater(rv2err,4*ae_machineepsilon*ae_fabs(ry.ptr.p_double[0], _state));
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_fabs(rv2-ry.ptr.p_double[0], _state),rv2err);
        
        /*
         * First test: X + X + ... + X = N*X
         */
        s = ae_exp(ae_maxint(pass, 50, _state), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        ry.ptr.p_double[0] = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        for(i=1; i<=n-1; i++)
        {
            ry.ptr.p_double[i] = ry.ptr.p_double[0];
        }
        for(i=0; i<=n-1; i++)
        {
            rx.ptr.p_double[i] = 1;
        }
        xdot(&rx, &ry, n, &temp, &rv2, &rv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(rv2err,0);
        exactnesserrors = exactnesserrors||ae_fp_greater(rv2err,4*ae_machineepsilon*ae_fabs(ry.ptr.p_double[0], _state)*n);
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_fabs(rv2-n*ry.ptr.p_double[0], _state),rv2err);
    }
    
    /*
     * test of precision: complex
     */
    n = 50000;
    ae_vector_set_length(&cx, n, _state);
    ae_vector_set_length(&cy, n, _state);
    ae_vector_set_length(&temp, 2*n, _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        ae_assert(n%2==0, "Assertion failed", _state);
        
        /*
         * First test: X + X + ... + X - X - X - ... - X = 1*X
         */
        s = ae_exp(ae_maxint(pass, 50, _state), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        cy.ptr.p_complex[0].x = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        cy.ptr.p_complex[0].y = (2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state);
        for(i=1; i<=n-1; i++)
        {
            cy.ptr.p_complex[i] = cy.ptr.p_complex[0];
        }
        for(i=0; i<=n/2-1; i++)
        {
            cx.ptr.p_complex[i] = ae_complex_from_d(1);
        }
        for(i=n/2; i<=n-2; i++)
        {
            cx.ptr.p_complex[i] = ae_complex_from_d(-1);
        }
        cx.ptr.p_complex[n-1] = ae_complex_from_d(0);
        xcdot(&cx, &cy, n, &temp, &cv2, &cv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(cv2err,0);
        exactnesserrors = exactnesserrors||ae_fp_greater(cv2err,4*ae_machineepsilon*ae_c_abs(cy.ptr.p_complex[0], _state));
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_c_abs(ae_c_sub(cv2,cy.ptr.p_complex[0]), _state),cv2err);
        
        /*
         * First test: X + X + ... + X = N*X
         */
        s = ae_exp(ae_maxint(pass, 50, _state), _state);
        if( pass==passcount-1&&pass>1 )
        {
            s = ae_maxrealnumber;
        }
        cy.ptr.p_complex[0] = ae_complex_from_d((2*ae_randomreal(_state)-1)*s*ae_sqrt(2*ae_randomreal(_state), _state));
        for(i=1; i<=n-1; i++)
        {
            cy.ptr.p_complex[i] = cy.ptr.p_complex[0];
        }
        for(i=0; i<=n-1; i++)
        {
            cx.ptr.p_complex[i] = ae_complex_from_d(1);
        }
        xcdot(&cx, &cy, n, &temp, &cv2, &cv2err, _state);
        exactnesserrors = exactnesserrors||ae_fp_less(cv2err,0);
        exactnesserrors = exactnesserrors||ae_fp_greater(cv2err,4*ae_machineepsilon*ae_c_abs(cy.ptr.p_complex[0], _state)*n);
        exactnesserrors = exactnesserrors||ae_fp_greater(ae_c_abs(ae_c_sub(cv2,ae_c_mul_d(cy.ptr.p_complex[0],n)), _state),cv2err);
    }
    
    /*
     * report
     */
    waserrors = approxerrors||exactnesserrors;
    if( !silent )
    {
        printf("TESTING XBLAS\n");
        printf("APPROX.TESTS:                            ");
        if( approxerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("EXACT TESTS:                             ");
        if( exactnesserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static ae_bool testdensesolverunit_rmatrixchecksolutionm(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdensesolverunit_rmatrixchecksolution(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdensesolverunit_rmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdensesolverunit_rmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdensesolverunit_cmatrixchecksolutionm(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdensesolverunit_cmatrixchecksolution(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state);
static ae_bool testdensesolverunit_cmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state);
static ae_bool testdensesolverunit_cmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state);
static void testdensesolverunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testdensesolverunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state);
static void testdensesolverunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testdensesolverunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state);
static void testdensesolverunit_testrsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdensesolverunit_testspdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdensesolverunit_testcsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdensesolverunit_testhpdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_bool* rfserrors,
     ae_state *_state);
static void testdensesolverunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state);
static void testdensesolverunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state);
static void testdensesolverunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state);
static void testdensesolverunit_cunset1d(/* Complex */ ae_vector* x,
     ae_state *_state);
static void testdensesolverunit_unsetrep(densesolverreport* r,
     ae_state *_state);
static void testdensesolverunit_unsetlsrep(densesolverlsreport* r,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testdensesolver(ae_bool silent, ae_state *_state)
{
    ae_int_t maxn;
    ae_int_t maxm;
    ae_int_t passcount;
    double threshold;
    ae_bool rerrors;
    ae_bool cerrors;
    ae_bool spderrors;
    ae_bool hpderrors;
    ae_bool rfserrors;
    ae_bool waserrors;
    ae_bool result;


    maxn = 10;
    maxm = 5;
    passcount = 5;
    threshold = 10000*ae_machineepsilon;
    rfserrors = ae_false;
    rerrors = ae_false;
    cerrors = ae_false;
    spderrors = ae_false;
    hpderrors = ae_false;
    testdensesolverunit_testrsolver(maxn, maxm, passcount, threshold, &rerrors, &rfserrors, _state);
    testdensesolverunit_testspdsolver(maxn, maxm, passcount, threshold, &spderrors, &rfserrors, _state);
    testdensesolverunit_testcsolver(maxn, maxm, passcount, threshold, &cerrors, &rfserrors, _state);
    testdensesolverunit_testhpdsolver(maxn, maxm, passcount, threshold, &hpderrors, &rfserrors, _state);
    waserrors = (((rerrors||cerrors)||spderrors)||hpderrors)||rfserrors;
    if( !silent )
    {
        printf("TESTING DENSE SOLVER\n");
        printf("* REAL:                                   ");
        if( rerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX:                                ");
        if( cerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPD:                                    ");
        if( spderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* HPD:                                    ");
        if( hpderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* ITERATIVE IMPROVEMENT:                  ");
        if( rfserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_rmatrixchecksolutionm(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_fp_less_eq(ae_fabs(xe->ptr.pp_double[i][j]-xs->ptr.pp_double[i][j], _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_rmatrixchecksolution(/* Real    */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xsm, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_move(&xsm.ptr.pp_double[0][0], xsm.stride, &xs->ptr.p_double[0], 1, ae_v_len(0,n-1));
    result = testdensesolverunit_rmatrixchecksolutionm(xe, n, 1, threshold, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_rmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,0)||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,0)||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
        if( info==-3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    result = result&&ae_fp_eq(xs->ptr.pp_double[i][j],0);
                }
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_rmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Real    */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xsm, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_move(&xsm.ptr.pp_double[0][0], xsm.stride, &xs->ptr.p_double[0], 1, ae_v_len(0,n-1));
    result = testdensesolverunit_rmatrixchecksingularm(n, 1, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_cmatrixchecksolutionm(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     ae_int_t m,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info<=0 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,100*ae_machineepsilon)||ae_fp_greater(rep->r1,1+1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,100*ae_machineepsilon)||ae_fp_greater(rep->rinf,1+1000*ae_machineepsilon));
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                result = result&&ae_fp_less_eq(ae_c_abs(ae_c_sub(xe->ptr.pp_complex[i][j],xs->ptr.pp_complex[i][j]), _state),threshold);
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results are correct solution.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_cmatrixchecksolution(/* Complex */ ae_matrix* xe,
     ae_int_t n,
     double threshold,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xsm, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_cmove(&xsm.ptr.pp_complex[0][0], xsm.stride, &xs->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    result = testdensesolverunit_cmatrixchecksolutionm(xe, n, 1, threshold, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_cmatrixchecksingularm(ae_int_t n,
     ae_int_t m,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_matrix* xs,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_bool result;


    result = ae_true;
    if( info!=-3&&info!=1 )
    {
        result = ae_false;
    }
    else
    {
        result = result&&!(ae_fp_less(rep->r1,0)||ae_fp_greater(rep->r1,1000*ae_machineepsilon));
        result = result&&!(ae_fp_less(rep->rinf,0)||ae_fp_greater(rep->rinf,1000*ae_machineepsilon));
        if( info==-3 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    result = result&&ae_c_eq_d(xs->ptr.pp_complex[i][j],0);
                }
            }
        }
    }
    return result;
}


/*************************************************************************
Checks whether solver results indicate singular matrix.
Returns True on success.
*************************************************************************/
static ae_bool testdensesolverunit_cmatrixchecksingular(ae_int_t n,
     ae_int_t info,
     densesolverreport* rep,
     /* Complex */ ae_vector* xs,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xsm;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xsm, 0, 0, DT_COMPLEX, _state, ae_true);

    ae_matrix_set_length(&xsm, n, 1, _state);
    ae_v_cmove(&xsm.ptr.pp_complex[0][0], xsm.stride, &xs->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    result = testdensesolverunit_cmatrixchecksingularm(n, 1, info, rep, &xsm, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testdensesolverunit_rmatrixmakeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
Copy
*************************************************************************/
static void testdensesolverunit_cmatrixmakeacopy(/* Complex */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Complex */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_complex[i][j] = a->ptr.pp_complex[i][j];
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testdensesolverunit_rmatrixdrophalf(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_double[i][j] = 1+2*i+3*j;
            }
        }
    }
}


/*************************************************************************
Drops upper or lower half of the matrix - fills it by special pattern
which may be used later to ensure that this part wasn't changed
*************************************************************************/
static void testdensesolverunit_cmatrixdrophalf(/* Complex */ ae_matrix* a,
     ae_int_t n,
     ae_bool droplower,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( (droplower&&i>j)||(!droplower&&i<j) )
            {
                a->ptr.pp_complex[i][j] = ae_complex_from_d(1+2*i+3*j);
            }
        }
    }
}


/*************************************************************************
Real test
*************************************************************************/
static void testdensesolverunit_testrsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* rerrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    double v;
    double verr;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bv, 0, DT_REAL, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                rmatrixrndcond(n, 1000, &a, _state);
                testdensesolverunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
                rmatrixlu(&lua, n, n, &p, _state);
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                        b.ptr.pp_double[i][j] = v;
                    }
                }
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset2d(&x, _state);
                rmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset2d(&x, _state);
                rmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset2d(&x, _state);
                rmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                
                /*
                 * Test DenseSolverRLS():
                 * * test on original system A*x = b
                 * * test on overdetermined system with the same solution: (A' A')'*x = (b' b')'
                 * * test on underdetermined system with the same solution: (A 0 0 0 ) * z = b
                 */
                info = 0;
                testdensesolverunit_unsetlsrep(&repls, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                rmatrixsolvels(&a, n, n, &bv, 0.0, &info, &repls, &xv, _state);
                if( info<=0 )
                {
                    *rerrors = ae_true;
                }
                else
                {
                    *rerrors = (*rerrors||ae_fp_less(repls.r2,100*ae_machineepsilon))||ae_fp_greater(repls.r2,1+1000*ae_machineepsilon);
                    *rerrors = (*rerrors||repls.n!=n)||repls.k!=0;
                    for(i=0; i<=n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xe.ptr.pp_double[i][0]-xv.ptr.p_double[i], _state),threshold);
                    }
                }
                info = 0;
                testdensesolverunit_unsetlsrep(&repls, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, 2*n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                ae_v_move(&bv.ptr.p_double[n], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(n,2*n-1));
                ae_matrix_set_length(&atmp, 2*n, n, _state);
                copymatrix(&a, 0, n-1, 0, n-1, &atmp, 0, n-1, 0, n-1, _state);
                copymatrix(&a, 0, n-1, 0, n-1, &atmp, n, 2*n-1, 0, n-1, _state);
                rmatrixsolvels(&atmp, 2*n, n, &bv, 0.0, &info, &repls, &xv, _state);
                if( info<=0 )
                {
                    *rerrors = ae_true;
                }
                else
                {
                    *rerrors = (*rerrors||ae_fp_less(repls.r2,100*ae_machineepsilon))||ae_fp_greater(repls.r2,1+1000*ae_machineepsilon);
                    *rerrors = (*rerrors||repls.n!=n)||repls.k!=0;
                    for(i=0; i<=n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xe.ptr.pp_double[i][0]-xv.ptr.p_double[i], _state),threshold);
                    }
                }
                info = 0;
                testdensesolverunit_unsetlsrep(&repls, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                ae_matrix_set_length(&atmp, n, 2*n, _state);
                copymatrix(&a, 0, n-1, 0, n-1, &atmp, 0, n-1, 0, n-1, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=n; j<=2*n-1; j++)
                    {
                        atmp.ptr.pp_double[i][j] = 0;
                    }
                }
                rmatrixsolvels(&atmp, n, 2*n, &bv, 0.0, &info, &repls, &xv, _state);
                if( info<=0 )
                {
                    *rerrors = ae_true;
                }
                else
                {
                    *rerrors = *rerrors||ae_fp_neq(repls.r2,0);
                    *rerrors = (*rerrors||repls.n!=2*n)||repls.k!=n;
                    for(i=0; i<=n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xe.ptr.pp_double[i][0]-xv.ptr.p_double[i], _state),threshold);
                    }
                    for(i=n; i<=2*n-1; i++)
                    {
                        *rerrors = *rerrors||ae_fp_greater(ae_fabs(xv.ptr.p_double[i], _state),threshold);
                    }
                }
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero
                 *    * with zero columns
                 *    * with zero rows
                 *    * with equal rows/columns
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=4; taskkind++)
                {
                    testdensesolverunit_unset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0;
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_move(&a.ptr.pp_double[0][0], a.stride, &a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1));
                    }
                    if( taskkind==4 )
                    {
                        
                        /*
                         * equal rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_move(&a.ptr.pp_double[0][0], 1, &a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                            b.ptr.pp_double[i][j] = v;
                        }
                    }
                    testdensesolverunit_rmatrixmakeacopy(&a, n, n, &lua, _state);
                    rmatrixlu(&lua, n, n, &p, _state);
                    
                    /*
                     * Test RMatrixSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    rmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                    *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test RMatrixSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    rmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                    *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test RMatrixLUSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    rmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test RMatrixLUSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    rmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test RMatrixMixedSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    rmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test RMatrixMixedSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    rmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *rerrors = *rerrors||!testdensesolverunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                }
            }
        }
    }
    
    /*
     * test iterative improvement
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test iterative improvement matrices
         *
         * A matrix/right part are constructed such that both matrix
         * and solution components are within (-1,+1). Such matrix/right part
         * have nice properties - system can be solved using iterative
         * improvement with |A*x-b| about several ulps of max(1,|b|).
         */
        n = 100;
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, 1, _state);
        ae_vector_set_length(&bv, n, _state);
        ae_vector_set_length(&tx, n, _state);
        ae_vector_set_length(&xv, n, _state);
        ae_vector_set_length(&y, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xv.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
            xdot(&y, &xv, n, &tx, &v, &verr, _state);
            bv.ptr.p_double[i] = v;
        }
        ae_v_move(&b.ptr.pp_double[0][0], b.stride, &bv.ptr.p_double[0], 1, ae_v_len(0,n-1));
        
        /*
         * Test RMatrixSolveM()
         */
        testdensesolverunit_unset2d(&x, _state);
        rmatrixsolvem(&a, n, &b, 1, ae_true, &info, &rep, &x, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            ae_vector_set_length(&xv, n, _state);
            ae_v_move(&xv.ptr.p_double[0], 1, &x.ptr.pp_double[0][0], x.stride, ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                xdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_fabs(v-b.ptr.pp_double[i][0], _state),8*ae_machineepsilon*ae_maxreal(1, ae_fabs(b.ptr.pp_double[i][0], _state), _state));
            }
        }
        
        /*
         * Test RMatrixSolve()
         */
        testdensesolverunit_unset1d(&xv, _state);
        rmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                xdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_fabs(v-bv.ptr.p_double[i], _state),8*ae_machineepsilon*ae_maxreal(1, ae_fabs(bv.ptr.p_double[i], _state), _state));
            }
        }
        
        /*
         * Test LS-solver on the same matrix
         */
        rmatrixsolvels(&a, n, n, &bv, 0.0, &info, &repls, &xv, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_move(&y.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
                xdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_fabs(v-bv.ptr.p_double[i], _state),8*ae_machineepsilon*ae_maxreal(1, ae_fabs(bv.ptr.p_double[i], _state), _state));
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
SPD test
*************************************************************************/
static void testdensesolverunit_testspdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* spderrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    double v;
    ae_bool isupper;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bv, 0, DT_REAL, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                isupper = ae_fp_greater(ae_randomreal(_state),0.5);
                spdmatrixrndcond(n, 1000, &a, _state);
                testdensesolverunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
                if( !spdmatrixcholesky(&cha, n, isupper, _state) )
                {
                    *spderrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                        b.ptr.pp_double[i][j] = v;
                    }
                }
                testdensesolverunit_rmatrixdrophalf(&a, n, isupper, _state);
                testdensesolverunit_rmatrixdrophalf(&cha, n, isupper, _state);
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset2d(&x, _state);
                spdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                spdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset2d(&x, _state);
                spdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_unset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                spdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero
                 *    * with zero columns
                 *    * with zero rows
                 *    * with equal rows/columns
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=3; taskkind++)
                {
                    testdensesolverunit_unset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 0;
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                        ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_muld(&a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1), 0);
                        ae_v_muld(&a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns/rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_move(&a.ptr.pp_double[0][0], a.stride, &a.ptr.pp_double[0][k], a.stride, ae_v_len(0,n-1));
                        ae_v_move(&a.ptr.pp_double[0][0], 1, &a.ptr.pp_double[k][0], 1, ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.pp_double[0][j], xe.stride, ae_v_len(0,n-1));
                            b.ptr.pp_double[i][j] = v;
                        }
                    }
                    testdensesolverunit_rmatrixmakeacopy(&a, n, n, &cha, _state);
                    testdensesolverunit_rmatrixdrophalf(&a, n, isupper, _state);
                    testdensesolverunit_rmatrixdrophalf(&cha, n, isupper, _state);
                    
                    /*
                     * Test SPDMatrixSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    spdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                    *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test SPDMatrixSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_unset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                    spdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                    *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * 'equal columns/rows' are degenerate, but
                     * Cholesky matrix with equal columns/rows IS NOT degenerate,
                     * so it is not used for testing purposes.
                     */
                    if( taskkind!=3 )
                    {
                        
                        /*
                         * Test SPDMatrixLUSolveM()
                         */
                        info = 0;
                        testdensesolverunit_unsetrep(&rep, _state);
                        testdensesolverunit_unset2d(&x, _state);
                        spdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                        *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksingularm(n, m, info, &rep, &x, _state);
                        
                        /*
                         * Test SPDMatrixLUSolve()
                         */
                        info = 0;
                        testdensesolverunit_unsetrep(&rep, _state);
                        testdensesolverunit_unset2d(&x, _state);
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_move(&bv.ptr.p_double[0], 1, &b.ptr.pp_double[0][0], b.stride, ae_v_len(0,n-1));
                        spdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                        *spderrors = *spderrors||!testdensesolverunit_rmatrixchecksingular(n, info, &rep, &xv, _state);
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Real test
*************************************************************************/
static void testdensesolverunit_testcsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* cerrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix lua;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    double verr;
    ae_complex v;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&lua, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&bv, 0, DT_COMPLEX, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&xv, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                cmatrixrndcond(n, 1000, &a, _state);
                testdensesolverunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
                cmatrixlu(&lua, n, n, &p, _state);
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                        xe.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                        b.ptr.pp_complex[i][j] = v;
                    }
                }
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset2d(&x, _state);
                cmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset2d(&x, _state);
                cmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset2d(&x, _state);
                cmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                cmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero
                 *    * with zero columns
                 *    * with zero rows
                 *    * with equal rows/columns
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=4; taskkind++)
                {
                    testdensesolverunit_cunset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_cmove(&a.ptr.pp_complex[0][0], a.stride, &a.ptr.pp_complex[0][k], a.stride, "N", ae_v_len(0,n-1));
                    }
                    if( taskkind==4 )
                    {
                        
                        /*
                         * equal rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_cmove(&a.ptr.pp_complex[0][0], 1, &a.ptr.pp_complex[k][0], 1, "N", ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                            b.ptr.pp_complex[i][j] = v;
                        }
                    }
                    testdensesolverunit_cmatrixmakeacopy(&a, n, n, &lua, _state);
                    cmatrixlu(&lua, n, n, &p, _state);
                    
                    /*
                     * Test CMatrixSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    cmatrixsolvem(&a, n, &b, m, ae_fp_greater(ae_randomreal(_state),0.5), &info, &rep, &x, _state);
                    *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test CMatrixSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    cmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
                    *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test CMatrixLUSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    cmatrixlusolvem(&lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test CMatrixLUSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    cmatrixlusolve(&lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * Test CMatrixMixedSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    cmatrixmixedsolvem(&a, &lua, &p, n, &b, m, &info, &rep, &x, _state);
                    *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test CMatrixMixedSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    cmatrixmixedsolve(&a, &lua, &p, n, &bv, &info, &rep, &xv, _state);
                    *cerrors = *cerrors||!testdensesolverunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                }
            }
        }
    }
    
    /*
     * test iterative improvement
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test iterative improvement matrices
         *
         * A matrix/right part are constructed such that both matrix
         * and solution components magnitudes are within (-1,+1).
         * Such matrix/right part have nice properties - system can
         * be solved using iterative improvement with |A*x-b| about
         * several ulps of max(1,|b|).
         */
        n = 100;
        ae_matrix_set_length(&a, n, n, _state);
        ae_matrix_set_length(&b, n, 1, _state);
        ae_vector_set_length(&bv, n, _state);
        ae_vector_set_length(&tx, 2*n, _state);
        ae_vector_set_length(&xv, n, _state);
        ae_vector_set_length(&y, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xv.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            xv.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
            }
            ae_v_cmove(&y.ptr.p_complex[0], 1, &a.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
            xcdot(&y, &xv, n, &tx, &v, &verr, _state);
            bv.ptr.p_complex[i] = v;
        }
        ae_v_cmove(&b.ptr.pp_complex[0][0], b.stride, &bv.ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
        
        /*
         * Test CMatrixSolveM()
         */
        testdensesolverunit_cunset2d(&x, _state);
        cmatrixsolvem(&a, n, &b, 1, ae_true, &info, &rep, &x, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            ae_vector_set_length(&xv, n, _state);
            ae_v_cmove(&xv.ptr.p_complex[0], 1, &x.ptr.pp_complex[0][0], x.stride, "N", ae_v_len(0,n-1));
            for(i=0; i<=n-1; i++)
            {
                ae_v_cmove(&y.ptr.p_complex[0], 1, &a.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
                xcdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_c_abs(ae_c_sub(v,b.ptr.pp_complex[i][0]), _state),8*ae_machineepsilon*ae_maxreal(1, ae_c_abs(b.ptr.pp_complex[i][0], _state), _state));
            }
        }
        
        /*
         * Test CMatrixSolve()
         */
        testdensesolverunit_cunset1d(&xv, _state);
        cmatrixsolve(&a, n, &bv, &info, &rep, &xv, _state);
        if( info<=0 )
        {
            *rfserrors = ae_true;
        }
        else
        {
            for(i=0; i<=n-1; i++)
            {
                ae_v_cmove(&y.ptr.p_complex[0], 1, &a.ptr.pp_complex[i][0], 1, "N", ae_v_len(0,n-1));
                xcdot(&y, &xv, n, &tx, &v, &verr, _state);
                *rfserrors = *rfserrors||ae_fp_greater(ae_c_abs(ae_c_sub(v,bv.ptr.p_complex[i]), _state),8*ae_machineepsilon*ae_maxreal(1, ae_c_abs(bv.ptr.p_complex[i], _state), _state));
            }
        }
        
        /*
         * TODO: Test LS-solver on the same matrix
         */
    }
    ae_frame_leave(_state);
}


/*************************************************************************
HPD test
*************************************************************************/
static void testdensesolverunit_testhpdsolver(ae_int_t maxn,
     ae_int_t maxm,
     ae_int_t passcount,
     double threshold,
     ae_bool* hpderrors,
     ae_bool* rfserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix cha;
    ae_matrix atmp;
    ae_vector p;
    ae_matrix xe;
    ae_matrix b;
    ae_vector bv;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t pass;
    ae_int_t taskkind;
    ae_complex v;
    ae_bool isupper;
    ae_int_t info;
    densesolverreport rep;
    densesolverlsreport repls;
    ae_matrix x;
    ae_vector xv;
    ae_vector y;
    ae_vector tx;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&cha, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&atmp, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&p, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&xe, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&bv, 0, DT_COMPLEX, _state, ae_true);
    _densesolverreport_init(&rep, _state, ae_true);
    _densesolverlsreport_init(&repls, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&xv, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&y, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&tx, 0, DT_COMPLEX, _state, ae_true);

    
    /*
     * General square matrices:
     * * test general solvers
     * * test least squares solver
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * ********************************************************
                 * WELL CONDITIONED TASKS
                 * ability to find correct solution is tested
                 * ********************************************************
                 *
                 * 1. generate random well conditioned matrix A.
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods on original A
                 */
                isupper = ae_fp_greater(ae_randomreal(_state),0.5);
                hpdmatrixrndcond(n, 1000, &a, _state);
                testdensesolverunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
                if( !hpdmatrixcholesky(&cha, n, isupper, _state) )
                {
                    *hpderrors = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                ae_matrix_set_length(&xe, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        xe.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                        xe.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                    }
                }
                ae_matrix_set_length(&b, n, m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                        b.ptr.pp_complex[i][j] = v;
                    }
                }
                testdensesolverunit_cmatrixdrophalf(&a, n, isupper, _state);
                testdensesolverunit_cmatrixdrophalf(&cha, n, isupper, _state);
                
                /*
                 * Test solvers
                 */
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset2d(&x, _state);
                hpdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                hpdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset2d(&x, _state);
                hpdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksolutionm(&xe, n, m, threshold, info, &rep, &x, _state);
                info = 0;
                testdensesolverunit_unsetrep(&rep, _state);
                testdensesolverunit_cunset1d(&xv, _state);
                ae_vector_set_length(&bv, n, _state);
                ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                hpdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksolution(&xe, n, threshold, info, &rep, &xv, _state);
                
                /*
                 * ********************************************************
                 * EXACTLY SINGULAR MATRICES
                 * ability to detect singularity is tested
                 * ********************************************************
                 *
                 * 1. generate different types of singular matrices:
                 *    * zero
                 *    * with zero columns
                 *    * with zero rows
                 *    * with equal rows/columns
                 * 2. generate random solution vector xe
                 * 3. generate right part b=A*xe
                 * 4. test different methods
                 */
                for(taskkind=0; taskkind<=3; taskkind++)
                {
                    testdensesolverunit_cunset2d(&a, _state);
                    if( taskkind==0 )
                    {
                        
                        /*
                         * all zeros
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j] = ae_complex_from_d(0);
                            }
                        }
                    }
                    if( taskkind==1 )
                    {
                        
                        /*
                         * there is zero column
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                                if( i==j )
                                {
                                    a.ptr.pp_complex[i][j].y = 0;
                                }
                                a.ptr.pp_complex[j][i] = a.ptr.pp_complex[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                        ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==2 )
                    {
                        
                        /*
                         * there is zero row
                         */
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                                if( i==j )
                                {
                                    a.ptr.pp_complex[i][j].y = 0;
                                }
                                a.ptr.pp_complex[j][i] = a.ptr.pp_complex[i][j];
                            }
                        }
                        k = ae_randominteger(n, _state);
                        ae_v_cmuld(&a.ptr.pp_complex[k][0], 1, ae_v_len(0,n-1), 0);
                        ae_v_cmuld(&a.ptr.pp_complex[0][k], a.stride, ae_v_len(0,n-1), 0);
                    }
                    if( taskkind==3 )
                    {
                        
                        /*
                         * equal columns/rows
                         */
                        if( n<2 )
                        {
                            continue;
                        }
                        ae_matrix_set_length(&a, n, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=i; j<=n-1; j++)
                            {
                                a.ptr.pp_complex[i][j].x = 2*ae_randomreal(_state)-1;
                                a.ptr.pp_complex[i][j].y = 2*ae_randomreal(_state)-1;
                                if( i==j )
                                {
                                    a.ptr.pp_complex[i][j].y = 0;
                                }
                                a.ptr.pp_complex[j][i] = a.ptr.pp_complex[i][j];
                            }
                        }
                        k = 1+ae_randominteger(n-1, _state);
                        ae_v_cmove(&a.ptr.pp_complex[0][0], a.stride, &a.ptr.pp_complex[0][k], a.stride, "N", ae_v_len(0,n-1));
                        ae_v_cmove(&a.ptr.pp_complex[0][0], 1, &a.ptr.pp_complex[k][0], 1, "N", ae_v_len(0,n-1));
                    }
                    ae_matrix_set_length(&xe, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            xe.ptr.pp_complex[i][j] = ae_complex_from_d(2*ae_randomreal(_state)-1);
                        }
                    }
                    ae_matrix_set_length(&b, n, m, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=m-1; j++)
                        {
                            v = ae_v_cdotproduct(&a.ptr.pp_complex[i][0], 1, "N", &xe.ptr.pp_complex[0][j], xe.stride, "N", ae_v_len(0,n-1));
                            b.ptr.pp_complex[i][j] = v;
                        }
                    }
                    testdensesolverunit_cmatrixmakeacopy(&a, n, n, &cha, _state);
                    testdensesolverunit_cmatrixdrophalf(&a, n, isupper, _state);
                    testdensesolverunit_cmatrixdrophalf(&cha, n, isupper, _state);
                    
                    /*
                     * Test SPDMatrixSolveM()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    hpdmatrixsolvem(&a, n, isupper, &b, m, &info, &rep, &x, _state);
                    *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                    
                    /*
                     * Test SPDMatrixSolve()
                     */
                    info = 0;
                    testdensesolverunit_unsetrep(&rep, _state);
                    testdensesolverunit_cunset2d(&x, _state);
                    ae_vector_set_length(&bv, n, _state);
                    ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                    hpdmatrixsolve(&a, n, isupper, &bv, &info, &rep, &xv, _state);
                    *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                    
                    /*
                     * 'equal columns/rows' are degenerate, but
                     * Cholesky matrix with equal columns/rows IS NOT degenerate,
                     * so it is not used for testing purposes.
                     */
                    if( taskkind!=3 )
                    {
                        
                        /*
                         * Test SPDMatrixLUSolveM()
                         */
                        info = 0;
                        testdensesolverunit_unsetrep(&rep, _state);
                        testdensesolverunit_cunset2d(&x, _state);
                        hpdmatrixcholeskysolvem(&cha, n, isupper, &b, m, &info, &rep, &x, _state);
                        *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksingularm(n, m, info, &rep, &x, _state);
                        
                        /*
                         * Test SPDMatrixLUSolve()
                         */
                        info = 0;
                        testdensesolverunit_unsetrep(&rep, _state);
                        testdensesolverunit_cunset2d(&x, _state);
                        ae_vector_set_length(&bv, n, _state);
                        ae_v_cmove(&bv.ptr.p_complex[0], 1, &b.ptr.pp_complex[0][0], b.stride, "N", ae_v_len(0,n-1));
                        hpdmatrixcholeskysolve(&cha, n, isupper, &bv, &info, &rep, &xv, _state);
                        *hpderrors = *hpderrors||!testdensesolverunit_cmatrixchecksingular(n, info, &rep, &xv, _state);
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testdensesolverunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testdensesolverunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testdensesolverunit_cunset2d(/* Complex */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testdensesolverunit_cunset1d(/* Complex */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_complex[0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets report
*************************************************************************/
static void testdensesolverunit_unsetrep(densesolverreport* r,
     ae_state *_state)
{


    r->r1 = -1;
    r->rinf = -1;
}


/*************************************************************************
Unsets report
*************************************************************************/
static void testdensesolverunit_unsetlsrep(densesolverlsreport* r,
     ae_state *_state)
{


    r->r2 = -1;
    r->n = -1;
    r->k = -1;
    testdensesolverunit_unset2d(&r->cx, _state);
}








ae_bool testlinmin(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool result;


    waserrors = ae_false;
    if( !silent )
    {
        printf("TESTING LINMIN\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}



static void testmincgunit_testfunc1(mincgstate* state, ae_state *_state);
static void testmincgunit_testfunc2(mincgstate* state, ae_state *_state);
static void testmincgunit_testfunc3(mincgstate* state, ae_state *_state);
static void testmincgunit_calciip2(mincgstate* state,
     ae_int_t n,
     ae_state *_state);
static void testmincgunit_calclowrank(mincgstate* state,
     ae_int_t n,
     ae_int_t vcnt,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* v,
     /* Real    */ ae_vector* vd,
     /* Real    */ ae_vector* x0,
     ae_state *_state);
static void testmincgunit_testpreconditioning(ae_bool* err,
     ae_state *_state);





ae_bool testmincg(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool referror;
    ae_bool eqerror;
    ae_bool linerror1;
    ae_bool linerror2;
    ae_bool restartserror;
    ae_bool precerror;
    ae_bool converror;
    ae_bool othererrors;
    ae_int_t n;
    ae_vector x;
    ae_vector xe;
    ae_vector b;
    ae_vector xlast;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_matrix a;
    ae_vector diagh;
    mincgstate state;
    mincgreport rep;
    ae_int_t cgtype;
    ae_int_t difftype;
    double diffstep;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _mincgstate_init(&state, _state, ae_true);
    _mincgreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    referror = ae_false;
    linerror1 = ae_false;
    linerror2 = ae_false;
    eqerror = ae_false;
    converror = ae_false;
    restartserror = ae_false;
    othererrors = ae_false;
    precerror = ae_false;
    testmincgunit_testpreconditioning(&precerror, _state);
    testother(&othererrors, _state);
    for(difftype=0; difftype<=1; difftype++)
    {
        for(cgtype=-1; cgtype<=1; cgtype++)
        {
            
            /*
             * Reference problem
             */
            ae_vector_set_length(&x, 2+1, _state);
            n = 3;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
            x.ptr.p_double[1] = 100*ae_randomreal(_state)-50;
            x.ptr.p_double[2] = 100*ae_randomreal(_state)-50;
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0]-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]-2)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                    state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                    state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                }
            }
            mincgresults(&state, &x, &rep, _state);
            referror = (((referror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-2, _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-2, _state),0.001);
            
            /*
             * F2 problem with restarts:
             * * make several iterations and restart BEFORE termination
             * * iterate and restart AFTER termination
             *
             * NOTE: step is bounded from above to avoid premature convergence
             */
            ae_vector_set_length(&x, 3, _state);
            n = 3;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            mincgsetstpmax(&state, 0.1, _state);
            mincgsetcond(&state, 0.0000001, 0.0, 0.0, 0, _state);
            for(i=0; i<=10; i++)
            {
                if( !mincgiteration(&state, _state) )
                {
                    break;
                }
                testmincgunit_testfunc2(&state, _state);
            }
            x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
            mincgrestartfrom(&state, &x, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc2(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log(2, _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log(2, _state), _state),0.01);
            x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
            x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
            mincgrestartfrom(&state, &x, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc2(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log(2, _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log(2, _state), _state),0.01);
            
            /*
             * 1D problem #1
             */
            ae_vector_set_length(&x, 0+1, _state);
            n = 1;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = -ae_cos(state.x.ptr.p_double[0], _state);
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = ae_sin(state.x.ptr.p_double[0], _state);
                }
            }
            mincgresults(&state, &x, &rep, _state);
            linerror1 = (linerror1||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]/ae_pi-ae_round(x.ptr.p_double[0]/ae_pi, _state), _state),0.001);
            
            /*
             * 1D problem #2
             */
            ae_vector_set_length(&x, 0+1, _state);
            n = 1;
            diffstep = 1.0E-6;
            x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0], _state)/(1+ae_sqr(state.x.ptr.p_double[0], _state));
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = (2*state.x.ptr.p_double[0]*(1+ae_sqr(state.x.ptr.p_double[0], _state))-ae_sqr(state.x.ptr.p_double[0], _state)*2*state.x.ptr.p_double[0])/ae_sqr(1+ae_sqr(state.x.ptr.p_double[0], _state), _state);
                }
            }
            mincgresults(&state, &x, &rep, _state);
            linerror2 = (linerror2||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0], _state),0.001);
            
            /*
             * Linear equations
             */
            diffstep = 1.0E-6;
            for(n=1; n<=10; n++)
            {
                
                /*
                 * Prepare task
                 */
                ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
                ae_vector_set_length(&x, n-1+1, _state);
                ae_vector_set_length(&xe, n-1+1, _state);
                ae_vector_set_length(&b, n-1+1, _state);
                for(i=0; i<=n-1; i++)
                {
                    xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    }
                    a.ptr.pp_double[i][i] = a.ptr.pp_double[i][i]+3*ae_sign(a.ptr.pp_double[i][i], _state);
                }
                for(i=0; i<=n-1; i++)
                {
                    v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    b.ptr.p_double[i] = v;
                }
                
                /*
                 * Solve task
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( difftype==0 )
                {
                    mincgcreate(n, &x, &state, _state);
                }
                if( difftype==1 )
                {
                    mincgcreatef(n, &x, diffstep, &state, _state);
                }
                mincgsetcgtype(&state, cgtype, _state);
                while(mincgiteration(&state, _state))
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = 0;
                    }
                    if( state.needfg )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            state.g.ptr.p_double[i] = 0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        if( state.needf||state.needfg )
                        {
                            state.f = state.f+ae_sqr(v-b.ptr.p_double[i], _state);
                        }
                        if( state.needfg )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                state.g.ptr.p_double[j] = state.g.ptr.p_double[j]+2*(v-b.ptr.p_double[i])*a.ptr.pp_double[i][j];
                            }
                        }
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                eqerror = eqerror||rep.terminationtype<=0;
                for(i=0; i<=n-1; i++)
                {
                    eqerror = eqerror||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001);
                }
            }
            
            /*
             * Testing convergence properties
             */
            diffstep = 1.0E-6;
            n = 3;
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.001, 0.0, 0.0, 0, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||rep.terminationtype!=4;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.0, 0.001, 0.0, 0, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||rep.terminationtype!=1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.0, 0.0, 0.001, 0, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||rep.terminationtype!=2;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            if( difftype==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( difftype==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0.0, 0.0, 0.0, 10, _state);
            mincgsetcgtype(&state, cgtype, _state);
            while(mincgiteration(&state, _state))
            {
                testmincgunit_testfunc3(&state, _state);
            }
            mincgresults(&state, &x, &rep, _state);
            converror = converror||!((rep.terminationtype==5&&rep.iterationscount==10)||rep.terminationtype==7);
        }
    }
    
    /*
     * end
     */
    waserrors = ((((((referror||eqerror)||linerror1)||linerror2)||converror)||othererrors)||restartserror)||precerror;
    if( !silent )
    {
        printf("TESTING CG OPTIMIZATION\n");
        printf("REFERENCE PROBLEM:                        ");
        if( referror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN-1 PROBLEM:                            ");
        if( linerror1 )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN-2 PROBLEM:                            ");
        if( linerror2 )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LINEAR EQUATIONS:                         ");
        if( eqerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RESTARTS:                                 ");
        if( restartserror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PRECONDITIONING:                          ");
        if( precerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Other properties
*************************************************************************/
void testother(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_vector x;
    ae_vector s;
    ae_vector a;
    ae_vector h;
    ae_vector xlast;
    double fprev;
    double xprev;
    double stpmax;
    ae_int_t i;
    mincgstate state;
    mincgreport rep;
    ae_int_t cgtype;
    double tmpeps;
    double epsg;
    double v;
    double r;
    ae_bool hasxlast;
    double lastscaledstep;
    ae_int_t pkind;
    ae_int_t ckind;
    ae_int_t mkind;
    ae_int_t dkind;
    double diffstep;
    double vc;
    double vm;
    ae_bool wasf;
    ae_bool wasfg;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    _mincgstate_init(&state, _state, ae_true);
    _mincgreport_init(&rep, _state, ae_true);

    for(cgtype=-1; cgtype<=1; cgtype++)
    {
        
        /*
         * Test reports (F should form monotone sequence)
         */
        n = 50;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 1;
        }
        mincgcreate(n, &x, &state, _state);
        mincgsetcond(&state, 0, 0, 0, 100, _state);
        mincgsetxrep(&state, ae_true, _state);
        fprev = ae_maxrealnumber;
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = 0;
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                }
            }
            if( state.xupdated )
            {
                *err = *err||ae_fp_greater(state.f,fprev);
                if( ae_fp_eq(fprev,ae_maxrealnumber) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *err = *err||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                    }
                }
                fprev = state.f;
                ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
        }
        mincgresults(&state, &x, &rep, _state);
        for(i=0; i<=n-1; i++)
        {
            *err = *err||ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]);
        }
        
        /*
         * Test differentiation vs. analytic gradient
         * (first one issues NeedF requests, second one issues NeedFG requests)
         */
        n = 50;
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 1;
            }
            if( dkind==0 )
            {
                mincgcreate(n, &x, &state, _state);
            }
            if( dkind==1 )
            {
                mincgcreatef(n, &x, diffstep, &state, _state);
            }
            mincgsetcond(&state, 0, 0, 0, n/2, _state);
            wasf = ae_false;
            wasfg = ae_false;
            while(mincgiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    }
                    if( state.needfg )
                    {
                        state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                    }
                }
                wasf = wasf||state.needf;
                wasfg = wasfg||state.needfg;
            }
            mincgresults(&state, &x, &rep, _state);
            if( dkind==0 )
            {
                *err = (*err||wasf)||!wasfg;
            }
            if( dkind==1 )
            {
                *err = (*err||!wasf)||wasfg;
            }
        }
        
        /*
         * Test that numerical differentiation uses scaling.
         *
         * In order to test that we solve simple optimization
         * problem: min(x^2) with initial x equal to 0.0.
         *
         * We choose random DiffStep and S, then we check that
         * optimizer evaluates function at +-DiffStep*S only.
         */
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&s, 1, _state);
        diffstep = ae_randomreal(_state)*1.0E-6;
        s.ptr.p_double[0] = ae_exp(ae_randomreal(_state)*4-2, _state);
        x.ptr.p_double[0] = 0;
        mincgcreatef(1, &x, diffstep, &state, _state);
        mincgsetcond(&state, 1.0E-6, 0, 0, 0, _state);
        mincgsetscale(&state, &s, _state);
        v = 0;
        while(mincgiteration(&state, _state))
        {
            state.f = ae_sqr(state.x.ptr.p_double[0], _state);
            v = ae_maxreal(v, ae_fabs(state.x.ptr.p_double[0], _state), _state);
        }
        mincgresults(&state, &x, &rep, _state);
        r = v/(s.ptr.p_double[0]*diffstep);
        *err = *err||ae_fp_greater(ae_fabs(ae_log(r, _state), _state),ae_log(1+1000*ae_machineepsilon, _state));
        
        /*
         * Test maximum step
         */
        n = 1;
        ae_vector_set_length(&x, n, _state);
        x.ptr.p_double[0] = 100;
        stpmax = 0.05+0.05*ae_randomreal(_state);
        mincgcreate(n, &x, &state, _state);
        mincgsetcond(&state, 1.0E-9, 0, 0, 0, _state);
        mincgsetstpmax(&state, stpmax, _state);
        mincgsetxrep(&state, ae_true, _state);
        xprev = x.ptr.p_double[0];
        while(mincgiteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
            }
            if( state.xupdated )
            {
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
                xprev = state.x.ptr.p_double[0];
            }
        }
        
        /*
         * Test correctness of the scaling:
         * * initial point is random point from [+1,+2]^N
         * * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
         * * we use random scaling matrix
         * * we test different variants of the preconditioning:
         *   0) unit preconditioner
         *   1) random diagonal from [0.01,100]
         *   2) scale preconditioner
         * * we set stringent stopping conditions (we try EpsG and EpsX)
         * * and we test that in the extremum stopping conditions are
         *   satisfied subject to the current scaling coefficients.
         */
        tmpeps = 1.0E-10;
        for(n=1; n<=10; n++)
        {
            for(pkind=0; pkind<=2; pkind++)
            {
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&xlast, n, _state);
                ae_vector_set_length(&a, n, _state);
                ae_vector_set_length(&s, n, _state);
                ae_vector_set_length(&h, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = ae_randomreal(_state)+1;
                    a.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                    s.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                    h.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                }
                mincgcreate(n, &x, &state, _state);
                mincgsetscale(&state, &s, _state);
                mincgsetxrep(&state, ae_true, _state);
                if( pkind==1 )
                {
                    mincgsetprecdiag(&state, &h, _state);
                }
                if( pkind==2 )
                {
                    mincgsetprecscale(&state, _state);
                }
                
                /*
                 * Test gradient-based stopping condition
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = ae_randomreal(_state)+1;
                }
                mincgsetcond(&state, tmpeps, 0, 0, 0, _state);
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 4, _state);
                            state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 3, _state);
                        }
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    v = v+ae_sqr(s.ptr.p_double[i]*4*a.ptr.p_double[i]*ae_pow(x.ptr.p_double[i], 3, _state), _state);
                }
                v = ae_sqrt(v, _state);
                *err = *err||ae_fp_greater(v,tmpeps);
                
                /*
                 * Test step-based stopping condition
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = ae_randomreal(_state)+1;
                }
                hasxlast = ae_false;
                mincgsetcond(&state, 0, 0, tmpeps, 0, _state);
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        state.f = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 4, _state);
                            state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 3, _state);
                        }
                    }
                    if( state.xupdated )
                    {
                        if( hasxlast )
                        {
                            lastscaledstep = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                lastscaledstep = lastscaledstep+ae_sqr(state.x.ptr.p_double[i]-xlast.ptr.p_double[i], _state)/ae_sqr(s.ptr.p_double[i], _state);
                            }
                            lastscaledstep = ae_sqrt(lastscaledstep, _state);
                        }
                        else
                        {
                            lastscaledstep = 0;
                        }
                        ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        hasxlast = ae_true;
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                *err = *err||ae_fp_greater(lastscaledstep,tmpeps);
            }
        }
        
        /*
         * Check correctness of the "trimming".
         *
         * Trimming is a technique which is used to help algorithm
         * cope with unbounded functions. In order to check this
         * technique we will try to solve following optimization
         * problem:
         *
         *     min f(x) subject to no constraints on X
         *            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
         *     f(x) = {
         *            { M, if x<=-0.999999 or x>=0.999999
         *
         * where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
         * (we try different combinations)
         */
        for(ckind=0; ckind<=1; ckind++)
        {
            for(mkind=0; mkind<=2; mkind++)
            {
                
                /*
                 * Choose c and M
                 */
                if( ckind==0 )
                {
                    vc = 1.0;
                }
                if( ckind==1 )
                {
                    vc = 1.0E+6;
                }
                if( mkind==0 )
                {
                    vm = 1.0E+8;
                }
                if( mkind==1 )
                {
                    vm = 1.0E+20;
                }
                if( mkind==2 )
                {
                    vm = _state->v_posinf;
                }
                
                /*
                 * Create optimizer, solve optimization problem
                 */
                epsg = 1.0E-6*vc;
                ae_vector_set_length(&x, 1, _state);
                x.ptr.p_double[0] = 0.0;
                mincgcreate(1, &x, &state, _state);
                mincgsetcond(&state, epsg, 0, 0, 0, _state);
                mincgsetcgtype(&state, cgtype, _state);
                while(mincgiteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        if( ae_fp_less(-0.999999,state.x.ptr.p_double[0])&&ae_fp_less(state.x.ptr.p_double[0],0.999999) )
                        {
                            state.f = 1/(1-state.x.ptr.p_double[0])+1/(1+state.x.ptr.p_double[0])+vc*state.x.ptr.p_double[0];
                            state.g.ptr.p_double[0] = 1/ae_sqr(1-state.x.ptr.p_double[0], _state)-1/ae_sqr(1+state.x.ptr.p_double[0], _state)+vc;
                        }
                        else
                        {
                            state.f = vm;
                        }
                    }
                }
                mincgresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                *err = *err||ae_fp_greater(ae_fabs(1/ae_sqr(1-x.ptr.p_double[0], _state)-1/ae_sqr(1+x.ptr.p_double[0], _state)+vc, _state),epsg);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Calculate test function #1
*************************************************************************/
static void testmincgunit_testfunc1(mincgstate* state, ae_state *_state)
{


    if( ae_fp_less(state->x.ptr.p_double[0],100) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state->x.ptr.p_double[1], _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 2*state->x.ptr.p_double[1];
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = 0;
            state->g.ptr.p_double[2] = 0;
        }
    }
}


/*************************************************************************
Calculate test function #2

Simple variation of #1, much more nonlinear, which makes unlikely premature
convergence of algorithm .
*************************************************************************/
static void testmincgunit_testfunc2(mincgstate* state, ae_state *_state)
{


    if( ae_fp_less(state->x.ptr.p_double[0],100) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state), _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 4*state->x.ptr.p_double[1]*ae_sqr(state->x.ptr.p_double[1], _state);
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = 0;
            state->g.ptr.p_double[2] = 0;
        }
    }
}


/*************************************************************************
Calculate test function #3

Simple variation of #1, much more nonlinear, with non-zero value at minimum.
It achieve two goals:
* makes unlikely premature convergence of algorithm .
* solves some issues with EpsF stopping condition which arise when
  F(minimum) is zero

*************************************************************************/
static void testmincgunit_testfunc3(mincgstate* state, ae_state *_state)
{
    double s;


    s = 0.001;
    if( ae_fp_less(state->x.ptr.p_double[0],100) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state)+s, _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 2*(ae_sqr(state->x.ptr.p_double[1], _state)+s)*2*state->x.ptr.p_double[1];
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = 0;
            state->g.ptr.p_double[2] = 0;
        }
    }
}


/*************************************************************************
Calculate test function IIP2

f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)

It has high condition number which makes fast convergence unlikely without
good preconditioner.

*************************************************************************/
static void testmincgunit_calciip2(mincgstate* state,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;


    if( state->needf||state->needfg )
    {
        state->f = 0;
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->needf||state->needfg )
        {
            state->f = state->f+ae_sqr(i*i+1, _state)*ae_sqr(state->x.ptr.p_double[i], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[i] = ae_sqr(i*i+1, _state)*2*state->x.ptr.p_double[i];
        }
    }
}


/*************************************************************************
Calculate test function f(x) = 0.5*(x-x0)'*A*(x-x0), A = D+V'*Vd*V
*************************************************************************/
static void testmincgunit_calclowrank(mincgstate* state,
     ae_int_t n,
     ae_int_t vcnt,
     /* Real    */ ae_vector* d,
     /* Real    */ ae_matrix* v,
     /* Real    */ ae_vector* vd,
     /* Real    */ ae_vector* x0,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double dx;
    double t;
    double t2;


    state->f = 0;
    for(i=0; i<=n-1; i++)
    {
        state->g.ptr.p_double[i] = 0;
    }
    for(i=0; i<=n-1; i++)
    {
        dx = state->x.ptr.p_double[i]-x0->ptr.p_double[i];
        state->f = state->f+0.5*dx*d->ptr.p_double[i]*dx;
        state->g.ptr.p_double[i] = state->g.ptr.p_double[i]+d->ptr.p_double[i]*dx;
    }
    for(i=0; i<=vcnt-1; i++)
    {
        t = 0;
        for(j=0; j<=n-1; j++)
        {
            t = t+v->ptr.pp_double[i][j]*(state->x.ptr.p_double[j]-x0->ptr.p_double[j]);
        }
        state->f = state->f+0.5*t*vd->ptr.p_double[i]*t;
        t2 = t*vd->ptr.p_double[i];
        ae_v_addd(&state->g.ptr.p_double[0], 1, &v->ptr.pp_double[i][0], 1, ae_v_len(0,n-1), t2);
    }
}


/*************************************************************************
This function tests preconditioning

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmincgunit_testpreconditioning(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_vector x;
    ae_vector x0;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t vs;
    ae_matrix v;
    ae_vector vd;
    ae_vector d;
    ae_vector s;
    ae_int_t cntb1;
    ae_int_t cntg1;
    ae_int_t cntb2;
    ae_int_t cntg2;
    double epsg;
    ae_vector diagh;
    mincgstate state;
    mincgreport rep;
    ae_int_t cgtype;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vd, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _mincgstate_init(&state, _state, ae_true);
    _mincgreport_init(&rep, _state, ae_true);

    k = 50;
    epsg = 1.0E-10;
    for(cgtype=-1; cgtype<=1; cgtype++)
    {
        
        /*
         * Preconditioner test 1.
         *
         * If
         * * B1 is default preconditioner
         * * G1 is diagonal precomditioner based on approximate diagonal of Hessian matrix
         * then "bad" preconditioner is worse than "good" one.
         * "Worse" means more iterations to converge.
         *
         *
         * We test it using f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1).
         *
         * N        - problem size
         * K        - number of repeated passes (should be large enough to average out random factors)
         */
        for(n=10; n<=15; n++)
        {
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 0;
            }
            mincgcreate(n, &x, &state, _state);
            mincgsetcgtype(&state, cgtype, _state);
            
            /*
             * Test it with default preconditioner
             */
            mincgsetprecdefault(&state, _state);
            cntb1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntb1 = cntb1+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            
            /*
             * Test it with perturbed diagonal preconditioner
             */
            ae_vector_set_length(&diagh, n, _state);
            for(i=0; i<=n-1; i++)
            {
                diagh.ptr.p_double[i] = 2*ae_sqr(i*i+1, _state)*(0.8+0.4*ae_randomreal(_state));
            }
            mincgsetprecdiag(&state, &diagh, _state);
            cntg1 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntg1 = cntg1+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            
            /*
             * Compare
             */
            *err = *err||cntb1<cntg1;
        }
        
        /*
         * Preconditioner test 2.
         *
         * If
         * * B1 is default preconditioner
         * * G1 is low rank exact preconditioner
         * then "bad" preconditioner is worse than "good" one.
         * "Worse" means more iterations to converge.
         *
         * Target function is f(x) = 0.5*(x-x0)'*A*(x-x0), A = D+V'*Vd*V
         *
         * N        - problem size
         * K        - number of repeated passes (should be large enough to average out random factors)
         */
        for(n=10; n<=15; n++)
        {
            for(vs=0; vs<=5; vs++)
            {
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&x0, n, _state);
                ae_vector_set_length(&d, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 0;
                    x0.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    d.ptr.p_double[i] = ae_exp(2*ae_randomreal(_state), _state);
                }
                if( vs>0 )
                {
                    ae_matrix_set_length(&v, vs, n, _state);
                    ae_vector_set_length(&vd, vs, _state);
                    for(i=0; i<=vs-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                        vd.ptr.p_double[i] = ae_exp(2*ae_randomreal(_state), _state);
                    }
                }
                mincgcreate(n, &x, &state, _state);
                mincgsetcgtype(&state, cgtype, _state);
                
                /*
                 * Test it with default preconditioner
                 */
                mincgsetprecdefault(&state, _state);
                cntb1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    mincgrestartfrom(&state, &x, _state);
                    while(mincgiteration(&state, _state))
                    {
                        testmincgunit_calclowrank(&state, n, vs, &d, &v, &vd, &x0, _state);
                    }
                    mincgresults(&state, &x, &rep, _state);
                    cntb1 = cntb1+rep.iterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                
                /*
                 * Test it with low rank preconditioner
                 */
                mincgsetpreclowrankfast(&state, &d, &vd, &v, vs, _state);
                cntg1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    mincgrestartfrom(&state, &x, _state);
                    while(mincgiteration(&state, _state))
                    {
                        testmincgunit_calclowrank(&state, n, vs, &d, &v, &vd, &x0, _state);
                    }
                    mincgresults(&state, &x, &rep, _state);
                    cntg1 = cntg1+rep.iterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                
                /*
                 * Compare
                 */
                *err = *err||cntb1<cntg1;
            }
        }
        
        /*
         * Preconditioner test 3.
         *
         * If
         * * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
         * * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
         * then B2 is worse than G2.
         * "Worse" means more iterations to converge.
         */
        for(n=10; n<=15; n++)
        {
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 0;
            }
            mincgcreate(n, &x, &state, _state);
            ae_vector_set_length(&s, n, _state);
            for(i=0; i<=n-1; i++)
            {
                s.ptr.p_double[i] = 1/ae_sqrt(2*ae_pow(i*i+1, 2, _state)*(0.8+0.4*ae_randomreal(_state)), _state);
            }
            mincgsetprecdefault(&state, _state);
            mincgsetscale(&state, &s, _state);
            cntb2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntb2 = cntb2+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            mincgsetprecscale(&state, _state);
            mincgsetscale(&state, &s, _state);
            cntg2 = 0;
            for(pass=0; pass<=k-1; pass++)
            {
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                mincgrestartfrom(&state, &x, _state);
                while(mincgiteration(&state, _state))
                {
                    testmincgunit_calciip2(&state, n, _state);
                }
                mincgresults(&state, &x, &rep, _state);
                cntg2 = cntg2+rep.iterationscount;
                *err = *err||rep.terminationtype<=0;
            }
            *err = *err||cntb2<cntg2;
        }
    }
    ae_frame_leave(_state);
}



static void testminbleicunit_checkbounds(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_int_t n,
     ae_bool* err,
     ae_state *_state);
static void testminbleicunit_calciip2(minbleicstate* state,
     ae_int_t n,
     ae_int_t fk,
     ae_state *_state);
static void testminbleicunit_testfeasibility(ae_bool* feaserr,
     ae_bool* converr,
     ae_bool* interr,
     ae_state *_state);
static void testminbleicunit_testother(ae_bool* err, ae_state *_state);
static void testminbleicunit_testconv(ae_bool* err, ae_state *_state);
static void testminbleicunit_testpreconditioning(ae_bool* err,
     ae_state *_state);
static void testminbleicunit_setrandompreconditioner(minbleicstate* state,
     ae_int_t n,
     ae_int_t preckind,
     ae_state *_state);





ae_bool testminbleic(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool feasibilityerrors;
    ae_bool othererrors;
    ae_bool precerrors;
    ae_bool interrors;
    ae_bool converrors;
    ae_bool result;


    waserrors = ae_false;
    feasibilityerrors = ae_false;
    othererrors = ae_false;
    precerrors = ae_false;
    interrors = ae_false;
    converrors = ae_false;
    testminbleicunit_testfeasibility(&feasibilityerrors, &converrors, &interrors, _state);
    testminbleicunit_testother(&othererrors, _state);
    testminbleicunit_testconv(&converrors, _state);
    testminbleicunit_testpreconditioning(&precerrors, _state);
    
    /*
     * end
     */
    waserrors = (((feasibilityerrors||othererrors)||converrors)||interrors)||precerrors;
    if( !silent )
    {
        printf("TESTING BLEIC OPTIMIZATION\n");
        printf("FEASIBILITY PROPERTIES:                   ");
        if( feasibilityerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PRECONDITIONING:                          ");
        if( precerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("INTERNAL ERRORS:                          ");
        if( interrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Checks that X is bounded with respect to BndL/BndU.

If it is not, True is assigned to the Err variable (which is not changed
otherwise).
*************************************************************************/
static void testminbleicunit_checkbounds(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* bndl,
     /* Real    */ ae_vector* bndu,
     ae_int_t n,
     ae_bool* err,
     ae_state *_state)
{
    ae_int_t i;


    for(i=0; i<=n-1; i++)
    {
        if( ae_fp_less(x->ptr.p_double[i],bndl->ptr.p_double[i])||ae_fp_greater(x->ptr.p_double[i],bndu->ptr.p_double[i]) )
        {
            *err = ae_true;
        }
    }
}


/*************************************************************************
Calculate test function IIP2

f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1)

It has high condition number which makes fast convergence unlikely without
good preconditioner.

*************************************************************************/
static void testminbleicunit_calciip2(minbleicstate* state,
     ae_int_t n,
     ae_int_t fk,
     ae_state *_state)
{
    ae_int_t i;


    if( state->needfg )
    {
        state->f = 0;
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->needfg )
        {
            state->f = state->f+ae_pow(i*i+1, 2*fk, _state)*ae_sqr(state->x.ptr.p_double[i], _state);
            state->g.ptr.p_double[i] = ae_pow(i*i+1, 2*fk, _state)*2*state->x.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function test feasibility properties.
It launches a sequence of problems and examines their solutions.
Most of the attention is directed towards feasibility properties,
although we make some quick checks to ensure that actual solution is found.

On failure sets FeasErr (or ConvErr, depending on failure type) to True,
or leaves it unchanged otherwise.

IntErr is set to True on internal errors (errors in the control flow).
*************************************************************************/
static void testminbleicunit_testfeasibility(ae_bool* feaserr,
     ae_bool* converr,
     ae_bool* interr,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pkind;
    ae_int_t preckind;
    ae_int_t passcount;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t nmax;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t p;
    double v;
    double v2;
    double v3;
    double vv;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector g;
    ae_vector x0;
    ae_vector xs;
    ae_matrix c;
    ae_vector ct;
    minbleicstate state;
    double epsc;
    double epsg;
    minbleicreport rep;
    ae_int_t dkind;
    double diffstep;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xs, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);

    nmax = 5;
    epsc = 1.0E-4;
    epsg = 1.0E-8;
    passcount = 10;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test problem 1:
         * * no boundary and inequality constraints
         * * randomly generated plane as equality constraint
         * * random point (not necessarily on the plane)
         * * f = |x|^P, P = {2, 4} is used as target function
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * either analytic gradient or numerical differentiation are used
         * * we check that after work is over we are on the plane and
         *   that we are in the stationary point of constrained F
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(pkind=1; pkind<=2; pkind++)
                {
                    for(n=1; n<=nmax; n++)
                    {
                        
                        /*
                         * Generate X, BL, BU, CT and left part of C.
                         *
                         * Right part of C is generated using somewhat complex algo:
                         * * we generate random vector and multiply it by C.
                         * * result is used as the right part.
                         * * calculations are done on the fly, vector itself is not stored
                         * We use such algo to be sure that our system is consistent.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&g, n, _state);
                        ae_matrix_set_length(&c, 1, n+1, _state);
                        ae_vector_set_length(&ct, 1, _state);
                        c.ptr.pp_double[0][n] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                            c.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                            v = 2*ae_randomreal(_state)-1;
                            c.ptr.pp_double[0][n] = c.ptr.pp_double[0][n]+c.ptr.pp_double[0][i]*v;
                        }
                        ct.ptr.p_int[0] = 0;
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbleiccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbleiccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbleicsetlc(&state, &c, &ct, 1, _state);
                        minbleicsetinnercond(&state, epsg, 0.0, 0.0, _state);
                        minbleicsetoutercond(&state, epsc, epsc, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = 0;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i], p, _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i], p-1, _state);
                                }
                            }
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * Test feasibility of solution
                         */
                        v = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        *feaserr = *feaserr||ae_fp_greater(ae_fabs(v-c.ptr.pp_double[0][n], _state),epsc);
                        
                        /*
                         * if C is nonzero, test that result is
                         * a stationary point of constrained F.
                         *
                         * NOTE: this check is done only if C is nonzero
                         */
                        vv = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                        if( ae_fp_neq(vv,0) )
                        {
                            
                            /*
                             * Calculate gradient at the result
                             * Project gradient into C
                             * Check projected norm
                             */
                            for(i=0; i<=n-1; i++)
                            {
                                g.ptr.p_double[i] = p*ae_pow(x.ptr.p_double[i], p-1, _state);
                            }
                            v2 = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                            v = ae_v_dotproduct(&c.ptr.pp_double[0][0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            vv = v/v2;
                            ae_v_subd(&g.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1), vv);
                            v3 = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            *converr = *converr||ae_fp_greater(ae_sqrt(v3, _state),0.001);
                        }
                    }
                }
            }
        }
        
        /*
         * Test problem 2 (multiple equality constraints):
         * * 1<=N<=NMax, 1<=K<=N
         * * no boundary constraints
         * * N-dimensional space
         * * randomly generated point xs
         * * K randomly generated hyperplanes which all pass through xs
         *   define K equality constraints: (a[k],x)=b[k]
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * f(x) = |x-x0|^2, x0 = xs+a[0]
         * * either analytic gradient or numerical differentiation are used
         * * extremum of f(x) is exactly xs because:
         *   * xs is the closest point in the plane defined by (a[0],x)=b[0]
         *   * xs is feasible by definition
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(n=2; n<=nmax; n++)
                {
                    for(k=1; k<=n; k++)
                    {
                        
                        /*
                         * Generate X, X0, XS, BL, BU, CT and left part of C.
                         *
                         * Right part of C is generated using somewhat complex algo:
                         * * we generate random vector and multiply it by C.
                         * * result is used as the right part.
                         * * calculations are done on the fly, vector itself is not stored
                         * We use such algo to be sure that our system is consistent.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&x0, n, _state);
                        ae_vector_set_length(&xs, n, _state);
                        ae_vector_set_length(&g, n, _state);
                        ae_matrix_set_length(&c, k, n+1, _state);
                        ae_vector_set_length(&ct, k, _state);
                        c.ptr.pp_double[0][n] = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                            xs.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &xs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            c.ptr.pp_double[i][n] = v;
                            ct.ptr.p_int[i] = 0;
                        }
                        ae_v_move(&x0.ptr.p_double[0], 1, &xs.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        ae_v_add(&x0.ptr.p_double[0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbleiccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbleiccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbleicsetlc(&state, &c, &ct, k, _state);
                        minbleicsetinnercond(&state, epsg, 0.0, 0.0, _state);
                        minbleicsetoutercond(&state, epsc, epsc, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = 0;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_sqr(state.x.ptr.p_double[i]-x0.ptr.p_double[i], _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = 2*(state.x.ptr.p_double[i]-x0.ptr.p_double[i]);
                                }
                            }
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * check feasiblity properties
                         */
                        for(i=0; i<=k-1; i++)
                        {
                            v = ae_v_dotproduct(&c.ptr.pp_double[i][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            *feaserr = *feaserr||ae_fp_greater(ae_fabs(v-c.ptr.pp_double[i][n], _state),epsc);
                        }
                        
                        /*
                         * Compare with XS
                         */
                        v = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+ae_sqr(x.ptr.p_double[i]-xs.ptr.p_double[i], _state);
                        }
                        v = ae_sqrt(v, _state);
                        *converr = *converr||ae_fp_greater(ae_fabs(v, _state),0.001);
                    }
                }
            }
        }
        
        /*
         * Another simple problem:
         * * bound constraints 0 <= x[i] <= 1
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple boundaries and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1)
         * * we also check that both final solution and subsequent iterates
         *   are strictly feasible
         */
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            for(preckind=0; preckind<=2; preckind++)
            {
                for(pkind=1; pkind<=2; pkind++)
                {
                    for(n=1; n<=nmax; n++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&bl, n, _state);
                        ae_vector_set_length(&bu, n, _state);
                        ae_vector_set_length(&x, n, _state);
                        ae_vector_set_length(&x0, n, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            bl.ptr.p_double[i] = 0;
                            bu.ptr.p_double[i] = 1;
                            x.ptr.p_double[i] = ae_randomreal(_state);
                            x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                        }
                        
                        /*
                         * Create and optimize
                         */
                        if( dkind==0 )
                        {
                            minbleiccreate(n, &x, &state, _state);
                        }
                        if( dkind==1 )
                        {
                            minbleiccreatef(n, &x, diffstep, &state, _state);
                        }
                        minbleicsetbc(&state, &bl, &bu, _state);
                        minbleicsetinnercond(&state, epsg, 0.0, 0.0, _state);
                        minbleicsetoutercond(&state, epsc, epsc, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needf||state.needfg )
                            {
                                state.f = 0;
                            }
                            for(i=0; i<=n-1; i++)
                            {
                                if( state.needf||state.needfg )
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], p, _state);
                                }
                                if( state.needfg )
                                {
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], p-1, _state);
                                }
                                *feaserr = *feaserr||ae_fp_less(state.x.ptr.p_double[i],0.0);
                                *feaserr = *feaserr||ae_fp_greater(state.x.ptr.p_double[i],1.0);
                            }
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        if( rep.terminationtype<=0 )
                        {
                            *converr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        
                        /*
                         * * compare solution with analytic one
                         * * check feasibility
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            *converr = *converr||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-boundval(x0.ptr.p_double[i], 0.0, 1.0, _state), _state),0.01);
                            *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],0.0);
                            *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],1.0);
                        }
                    }
                }
            }
        }
        
        /*
         * Same as previous one, but with bound constraints posed
         * as general linear ones:
         * * no bound constraints
         * * 2*N linear constraints 0 <= x[i] <= 1
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from converging
         *   to the feasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * with such simple constraints and function it is easy to find
         *   analytic form of solution: S[i] = bound(x0[i], 0, 1).
         * * however, we can't guarantee that solution is strictly feasible
         *   with respect to nonlinearity constraint, so we check
         *   for approximate feasibility.
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    
                    /*
                     * Generate X, BL, BU.
                     */
                    p = 2*pkind;
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    ae_matrix_set_length(&c, 2*n, n+1, _state);
                    ae_vector_set_length(&ct, 2*n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = ae_randomreal(_state);
                        x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                        for(j=0; j<=n; j++)
                        {
                            c.ptr.pp_double[2*i+0][j] = 0;
                            c.ptr.pp_double[2*i+1][j] = 0;
                        }
                        c.ptr.pp_double[2*i+0][i] = 1;
                        c.ptr.pp_double[2*i+0][n] = 0;
                        ct.ptr.p_int[2*i+0] = 1;
                        c.ptr.pp_double[2*i+1][i] = 1;
                        c.ptr.pp_double[2*i+1][n] = 1;
                        ct.ptr.p_int[2*i+1] = -1;
                    }
                    
                    /*
                     * Create and optimize
                     */
                    minbleiccreate(n, &x, &state, _state);
                    minbleicsetlc(&state, &c, &ct, 2*n, _state);
                    minbleicsetinnercond(&state, epsg, 0.0, 0.0, _state);
                    minbleicsetoutercond(&state, epsc, epsc, _state);
                    testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], p, _state);
                                state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], p-1, _state);
                            }
                            continue;
                        }
                        
                        /*
                         * Unknown protocol specified
                         */
                        *interr = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    if( rep.terminationtype<=0 )
                    {
                        *converr = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    
                    /*
                     * * compare solution with analytic one
                     * * check feasibility
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        *converr = *converr||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-boundval(x0.ptr.p_double[i], 0.0, 1.0, _state), _state),0.05);
                        *feaserr = *feaserr||ae_fp_less(x.ptr.p_double[i],0.0-epsc);
                        *feaserr = *feaserr||ae_fp_greater(x.ptr.p_double[i],1.0+epsc);
                    }
                }
            }
        }
        
        /*
         * Infeasible problem:
         * * all bound constraints are 0 <= x[i] <= 1 except for one
         * * that one is 0 >= x[i] >= 1
         * * no linear constraints
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from detecting
         *   infeasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x-x0|^P, where P={2,4} and x0 is randomly selected from [-1,+2]^N
         * * algorithm must return correct error code on such problem
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    
                    /*
                     * Generate X, BL, BU.
                     */
                    p = 2*pkind;
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&x0, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bl.ptr.p_double[i] = 0;
                        bu.ptr.p_double[i] = 1;
                        x.ptr.p_double[i] = ae_randomreal(_state);
                        x0.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                    }
                    i = ae_randominteger(n, _state);
                    bl.ptr.p_double[i] = 1;
                    bu.ptr.p_double[i] = 0;
                    
                    /*
                     * Create and optimize
                     */
                    minbleiccreate(n, &x, &state, _state);
                    minbleicsetbc(&state, &bl, &bu, _state);
                    minbleicsetinnercond(&state, epsg, 0.0, 0.0, _state);
                    minbleicsetoutercond(&state, epsc, epsc, _state);
                    testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], p, _state);
                                state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i]-x0.ptr.p_double[i], p-1, _state);
                            }
                            continue;
                        }
                        
                        /*
                         * Unknown protocol specified
                         */
                        *interr = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    *feaserr = *feaserr||rep.terminationtype!=-3;
                }
            }
        }
        
        /*
         * Infeasible problem (2):
         * * no bound and inequality constraints
         * * 1<=K<=N arbitrary equality constraints
         * * (K+1)th constraint which is equal to the first constraint a*x=c,
         *   but with c:=c+1. I.e. we have both a*x=c and a*x=c+1, which can't
         *   be true (other constraints may be inconsistent too, but we don't
         *   have to check it).
         * * preconditioner is chosen at random (we just want to be
         *   sure that preconditioning won't prevent us from detecting
         *   infeasible point):
         *   * unit preconditioner
         *   * random diagonal-based preconditioner
         *   * random scale-based preconditioner
         * * F(x) = |x|^P, where P={2,4}
         * * algorithm must return correct error code on such problem
         */
        for(preckind=0; preckind<=2; preckind++)
        {
            for(pkind=1; pkind<=2; pkind++)
            {
                for(n=1; n<=nmax; n++)
                {
                    for(k=1; k<=n; k++)
                    {
                        
                        /*
                         * Generate X, BL, BU.
                         */
                        p = 2*pkind;
                        ae_vector_set_length(&x, n, _state);
                        ae_matrix_set_length(&c, k+1, n+1, _state);
                        ae_vector_set_length(&ct, k+1, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            x.ptr.p_double[i] = ae_randomreal(_state);
                        }
                        for(i=0; i<=k-1; i++)
                        {
                            for(j=0; j<=n; j++)
                            {
                                c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            }
                            ct.ptr.p_int[i] = 0;
                        }
                        ct.ptr.p_int[k] = 0;
                        ae_v_move(&c.ptr.pp_double[k][0], 1, &c.ptr.pp_double[0][0], 1, ae_v_len(0,n-1));
                        c.ptr.pp_double[k][n] = c.ptr.pp_double[0][n]+1;
                        
                        /*
                         * Create and optimize
                         */
                        minbleiccreate(n, &x, &state, _state);
                        minbleicsetlc(&state, &c, &ct, k+1, _state);
                        minbleicsetinnercond(&state, epsg, 0.0, 0.0, _state);
                        minbleicsetoutercond(&state, epsc, epsc, _state);
                        testminbleicunit_setrandompreconditioner(&state, n, preckind, _state);
                        while(minbleiciteration(&state, _state))
                        {
                            if( state.needfg )
                            {
                                state.f = 0;
                                for(i=0; i<=n-1; i++)
                                {
                                    state.f = state.f+ae_pow(state.x.ptr.p_double[i], p, _state);
                                    state.g.ptr.p_double[i] = p*ae_pow(state.x.ptr.p_double[i], p-1, _state);
                                }
                                continue;
                            }
                            
                            /*
                             * Unknown protocol specified
                             */
                            *interr = ae_true;
                            ae_frame_leave(_state);
                            return;
                        }
                        minbleicresults(&state, &x, &rep, _state);
                        *feaserr = *feaserr||rep.terminationtype!=-3;
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function additional properties.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbleicunit_testother(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t nmax;
    ae_int_t i;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_vector xf;
    ae_vector xlast;
    ae_vector a;
    ae_vector s;
    ae_vector h;
    ae_matrix c;
    ae_vector ct;
    double fprev;
    double xprev;
    double stpmax;
    double v;
    ae_int_t pkind;
    ae_int_t ckind;
    ae_int_t mkind;
    double vc;
    double vm;
    minbleicstate state;
    double epsc;
    double epsg;
    double tmpeps;
    minbleicreport rep;
    double diffstep;
    ae_int_t dkind;
    ae_bool wasf;
    ae_bool wasfg;
    double r;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xf, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);

    nmax = 5;
    epsc = 1.0E-4;
    epsg = 1.0E-8;
    passcount = 10;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test reports:
         * * first value must be starting point
         * * last value must be last point
         */
        n = 50;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 10;
            bl.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            bu.ptr.p_double[i] = _state->v_posinf;
        }
        minbleiccreate(n, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetinnercond(&state, 0, 0, 0, _state);
        minbleicsetmaxits(&state, 10, _state);
        minbleicsetoutercond(&state, 1.0E-64, 1.0E-64, _state);
        minbleicsetxrep(&state, ae_true, _state);
        fprev = ae_maxrealnumber;
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = 0;
                for(i=0; i<=n-1; i++)
                {
                    state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                }
            }
            if( state.xupdated )
            {
                if( ae_fp_eq(fprev,ae_maxrealnumber) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        *err = *err||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                    }
                }
                fprev = state.f;
                ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        for(i=0; i<=n-1; i++)
        {
            *err = *err||ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]);
        }
        
        /*
         * Test differentiation vs. analytic gradient
         * (first one issues NeedF requests, second one issues NeedFG requests)
         */
        n = 50;
        diffstep = 1.0E-6;
        for(dkind=0; dkind<=1; dkind++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xlast, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 1;
            }
            if( dkind==0 )
            {
                minbleiccreate(n, &x, &state, _state);
            }
            if( dkind==1 )
            {
                minbleiccreatef(n, &x, diffstep, &state, _state);
            }
            minbleicsetinnercond(&state, 1.0E-10, 0, 0, _state);
            minbleicsetoutercond(&state, 1.0E-6, 1.0E-6, _state);
            wasf = ae_false;
            wasfg = ae_false;
            while(minbleiciteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = 0;
                }
                for(i=0; i<=n-1; i++)
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                    }
                    if( state.needfg )
                    {
                        state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                    }
                }
                wasf = wasf||state.needf;
                wasfg = wasfg||state.needfg;
            }
            minbleicresults(&state, &x, &rep, _state);
            if( dkind==0 )
            {
                *err = (*err||wasf)||!wasfg;
            }
            if( dkind==1 )
            {
                *err = (*err||!wasf)||wasfg;
            }
        }
        
        /*
         * Test that numerical differentiation uses scaling.
         *
         * In order to test that we solve simple optimization
         * problem: min(x^2) with initial x equal to 0.0.
         *
         * We choose random DiffStep and S, then we check that
         * optimizer evaluates function at +-DiffStep*S only.
         */
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&s, 1, _state);
        diffstep = ae_randomreal(_state)*1.0E-6;
        s.ptr.p_double[0] = ae_exp(ae_randomreal(_state)*4-2, _state);
        x.ptr.p_double[0] = 0;
        minbleiccreatef(1, &x, diffstep, &state, _state);
        minbleicsetinnercond(&state, 1.0E-6, 0, 0, _state);
        minbleicsetscale(&state, &s, _state);
        v = 0;
        while(minbleiciteration(&state, _state))
        {
            state.f = ae_sqr(state.x.ptr.p_double[0], _state);
            v = ae_maxreal(v, ae_fabs(state.x.ptr.p_double[0], _state), _state);
        }
        minbleicresults(&state, &x, &rep, _state);
        r = v/(s.ptr.p_double[0]*diffstep);
        *err = *err||ae_fp_greater(ae_fabs(ae_log(r, _state), _state),ae_log(1+1000*ae_machineepsilon, _state));
        
        /*
         * Test stpmax
         */
        n = 1;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        x.ptr.p_double[0] = 100;
        bl.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        bu.ptr.p_double[0] = _state->v_posinf;
        stpmax = 0.05+0.05*ae_randomreal(_state);
        minbleiccreate(n, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetinnercond(&state, epsg, 0, 0, _state);
        minbleicsetoutercond(&state, epsc, epsc, _state);
        minbleicsetxrep(&state, ae_true, _state);
        minbleicsetstpmax(&state, stpmax, _state);
        xprev = x.ptr.p_double[0];
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
            }
            if( state.xupdated )
            {
                *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
                xprev = state.x.ptr.p_double[0];
            }
        }
        
        /*
         * Ability to solve problems with function which is unbounded from below
         */
        n = 1;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&bl, n, _state);
        ae_vector_set_length(&bu, n, _state);
        bl.ptr.p_double[0] = 4*ae_randomreal(_state)+1;
        bu.ptr.p_double[0] = bl.ptr.p_double[0]+1;
        x.ptr.p_double[0] = 0.5*(bl.ptr.p_double[0]+bu.ptr.p_double[0]);
        minbleiccreate(n, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetinnercond(&state, epsg, 0, 0, _state);
        minbleicsetoutercond(&state, epsc, epsc, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = -1.0E8*ae_sqr(state.x.ptr.p_double[0], _state);
                state.g.ptr.p_double[0] = -2.0E8*state.x.ptr.p_double[0];
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-bu.ptr.p_double[0], _state),epsc);
        
        /*
         * Test correctness of the scaling:
         * * initial point is random point from [+1,+2]^N
         * * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
         * * function is EFFECTIVELY unconstrained; it has formal constraints,
         *   but they are inactive at the solution; we try different variants
         *   in order to explore different control paths of the optimizer:
         *   0) absense of constraints
         *   1) bound constraints -100000<=x[i]<=100000
         *   2) one linear constraint 0*x=0
         *   3) combination of (1) and (2)
         * * we use random scaling matrix
         * * we test different variants of the preconditioning:
         *   0) unit preconditioner
         *   1) random diagonal from [0.01,100]
         *   2) scale preconditioner
         * * we set very mild outer stopping conditions - OuterEpsX=1.0, but
         *   inner conditions are very stringent
         * * and we test that in the extremum inner stopping conditions are
         *   satisfied subject to the current scaling coefficients.
         */
        tmpeps = 1.0E-10;
        for(n=1; n<=10; n++)
        {
            for(ckind=0; ckind<=3; ckind++)
            {
                for(pkind=0; pkind<=2; pkind++)
                {
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&a, n, _state);
                    ae_vector_set_length(&s, n, _state);
                    ae_vector_set_length(&h, n, _state);
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    ae_matrix_set_length(&c, 1, n+1, _state);
                    ae_vector_set_length(&ct, 1, _state);
                    ct.ptr.p_int[0] = 0;
                    c.ptr.pp_double[0][n] = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = ae_randomreal(_state)+1;
                        bl.ptr.p_double[i] = -100000;
                        bu.ptr.p_double[i] = 100000;
                        c.ptr.pp_double[0][i] = 0;
                        a.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                        s.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                        h.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                    }
                    minbleiccreate(n, &x, &state, _state);
                    if( ckind==1||ckind==3 )
                    {
                        minbleicsetbc(&state, &bl, &bu, _state);
                    }
                    if( ckind==2||ckind==3 )
                    {
                        minbleicsetlc(&state, &c, &ct, 1, _state);
                    }
                    if( pkind==1 )
                    {
                        minbleicsetprecdiag(&state, &h, _state);
                    }
                    if( pkind==2 )
                    {
                        minbleicsetprecscale(&state, _state);
                    }
                    minbleicsetinnercond(&state, tmpeps, 0, 0, _state);
                    minbleicsetoutercond(&state, 1.0, 1.0E-8, _state);
                    minbleicsetscale(&state, &s, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        if( state.needfg )
                        {
                            state.f = 0;
                            for(i=0; i<=n-1; i++)
                            {
                                state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 4, _state);
                                state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 3, _state);
                            }
                        }
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    if( rep.terminationtype<=0 )
                    {
                        *err = ae_true;
                        ae_frame_leave(_state);
                        return;
                    }
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        v = v+ae_sqr(s.ptr.p_double[i]*4*a.ptr.p_double[i]*ae_pow(x.ptr.p_double[i], 3, _state), _state);
                    }
                    v = ae_sqrt(v, _state);
                    *err = *err||ae_fp_greater(v,tmpeps);
                }
            }
        }
        
        /*
         * Check correctness of the "trimming".
         *
         * Trimming is a technique which is used to help algorithm
         * cope with unbounded functions. In order to check this
         * technique we will try to solve following optimization
         * problem:
         *
         *     min f(x) subject to no constraints on X
         *            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
         *     f(x) = {
         *            { M, if x<=-0.999999 or x>=0.999999
         *
         * where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
         * (we try different combinations)
         */
        for(ckind=0; ckind<=1; ckind++)
        {
            for(mkind=0; mkind<=2; mkind++)
            {
                
                /*
                 * Choose c and M
                 */
                if( ckind==0 )
                {
                    vc = 1.0;
                }
                if( ckind==1 )
                {
                    vc = 1.0E+6;
                }
                if( mkind==0 )
                {
                    vm = 1.0E+8;
                }
                if( mkind==1 )
                {
                    vm = 1.0E+20;
                }
                if( mkind==2 )
                {
                    vm = _state->v_posinf;
                }
                
                /*
                 * Create optimizer, solve optimization problem
                 */
                epsg = 1.0E-6*vc;
                ae_vector_set_length(&x, 1, _state);
                x.ptr.p_double[0] = 0.0;
                minbleiccreate(1, &x, &state, _state);
                minbleicsetinnercond(&state, epsg, 0, 0, _state);
                minbleicsetoutercond(&state, 1.0E-6, 1.0E-6, _state);
                while(minbleiciteration(&state, _state))
                {
                    if( state.needfg )
                    {
                        if( ae_fp_less(-0.999999,state.x.ptr.p_double[0])&&ae_fp_less(state.x.ptr.p_double[0],0.999999) )
                        {
                            state.f = 1/(1-state.x.ptr.p_double[0])+1/(1+state.x.ptr.p_double[0])+vc*state.x.ptr.p_double[0];
                            state.g.ptr.p_double[0] = 1/ae_sqr(1-state.x.ptr.p_double[0], _state)-1/ae_sqr(1+state.x.ptr.p_double[0], _state)+vc;
                        }
                        else
                        {
                            state.f = vm;
                        }
                    }
                }
                minbleicresults(&state, &x, &rep, _state);
                if( rep.terminationtype<=0 )
                {
                    *err = ae_true;
                    ae_frame_leave(_state);
                    return;
                }
                *err = *err||ae_fp_greater(ae_fabs(1/ae_sqr(1-x.ptr.p_double[0], _state)-1/ae_sqr(1+x.ptr.p_double[0], _state)+vc, _state),epsg);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests convergence properties.
We solve several simple problems with different combinations of constraints

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbleicunit_testconv(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_int_t pass;
    ae_vector bl;
    ae_vector bu;
    ae_vector x;
    ae_matrix c;
    ae_vector ct;
    minbleicstate state;
    double epsc;
    double epsg;
    double tol;
    minbleicreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);

    epsc = 1.0E-4;
    epsg = 1.0E-8;
    tol = 0.001;
    passcount = 10;
    
    /*
     * Three closely connected problems:
     * * 2-dimensional space
     * * octagonal area bounded by:
     *   * -1<=x<=+1
     *   * -1<=y<=+1
     *   * x+y<=1.5
     *   * x-y<=1.5
     *   * -x+y<=1.5
     *   * -x-y<=1.5
     * * several target functions:
     *   * f0=x+0.001*y, minimum at x=-1, y=-0.5
     *   * f1=(x+10)^2+y^2, minimum at x=-1, y=0
     *   * f2=(x+10)^2+(y-0.6)^2, minimum at x=-1, y=0.5
     */
    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&bl, 2, _state);
    ae_vector_set_length(&bu, 2, _state);
    ae_matrix_set_length(&c, 4, 3, _state);
    ae_vector_set_length(&ct, 4, _state);
    bl.ptr.p_double[0] = -1;
    bl.ptr.p_double[1] = -1;
    bu.ptr.p_double[0] = 1;
    bu.ptr.p_double[1] = 1;
    c.ptr.pp_double[0][0] = 1;
    c.ptr.pp_double[0][1] = 1;
    c.ptr.pp_double[0][2] = 1.5;
    ct.ptr.p_int[0] = -1;
    c.ptr.pp_double[1][0] = 1;
    c.ptr.pp_double[1][1] = -1;
    c.ptr.pp_double[1][2] = 1.5;
    ct.ptr.p_int[1] = -1;
    c.ptr.pp_double[2][0] = -1;
    c.ptr.pp_double[2][1] = 1;
    c.ptr.pp_double[2][2] = 1.5;
    ct.ptr.p_int[2] = -1;
    c.ptr.pp_double[3][0] = -1;
    c.ptr.pp_double[3][1] = -1;
    c.ptr.pp_double[3][2] = 1.5;
    ct.ptr.p_int[3] = -1;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * f0
         */
        x.ptr.p_double[0] = 0.2*ae_randomreal(_state)-0.1;
        x.ptr.p_double[1] = 0.2*ae_randomreal(_state)-0.1;
        minbleiccreate(2, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, 4, _state);
        minbleicsetinnercond(&state, epsg, 0, 0, _state);
        minbleicsetoutercond(&state, epsc, epsc, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = state.x.ptr.p_double[0]+0.001*state.x.ptr.p_double[1];
                state.g.ptr.p_double[0] = 1;
                state.g.ptr.p_double[1] = 0.001;
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]+1, _state),tol);
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[1]+0.5, _state),tol);
        }
        else
        {
            *err = ae_true;
        }
        
        /*
         * f1
         */
        x.ptr.p_double[0] = 0.2*ae_randomreal(_state)-0.1;
        x.ptr.p_double[1] = 0.2*ae_randomreal(_state)-0.1;
        minbleiccreate(2, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, 4, _state);
        minbleicsetinnercond(&state, epsg, 0, 0, _state);
        minbleicsetoutercond(&state, epsc, epsc, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]+10, _state)+ae_sqr(state.x.ptr.p_double[1], _state);
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+10);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]+1, _state),tol);
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),tol);
        }
        else
        {
            *err = ae_true;
        }
        
        /*
         * f2
         */
        x.ptr.p_double[0] = 0.2*ae_randomreal(_state)-0.1;
        x.ptr.p_double[1] = 0.2*ae_randomreal(_state)-0.1;
        minbleiccreate(2, &x, &state, _state);
        minbleicsetbc(&state, &bl, &bu, _state);
        minbleicsetlc(&state, &c, &ct, 4, _state);
        minbleicsetinnercond(&state, epsg, 0, 0, _state);
        minbleicsetoutercond(&state, epsc, epsc, _state);
        while(minbleiciteration(&state, _state))
        {
            if( state.needfg )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]+10, _state)+ae_sqr(state.x.ptr.p_double[1]-0.6, _state);
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]+10);
                state.g.ptr.p_double[1] = 2*(state.x.ptr.p_double[1]-0.6);
            }
        }
        minbleicresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[0]+1, _state),tol);
            *err = *err||ae_fp_greater(ae_fabs(x.ptr.p_double[1]-0.5, _state),tol);
        }
        else
        {
            *err = ae_true;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests preconditioning

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminbleicunit_testpreconditioning(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_vector x;
    ae_vector x0;
    ae_int_t i;
    ae_int_t k;
    ae_matrix v;
    ae_matrix c;
    ae_vector ct;
    ae_vector bl;
    ae_vector bu;
    ae_vector vd;
    ae_vector d;
    ae_vector units;
    ae_vector s;
    ae_int_t cntb1;
    ae_int_t cntb2;
    ae_int_t cntg1;
    ae_int_t cntg2;
    double epsg;
    ae_vector diagh;
    minbleicstate state;
    minbleicreport rep;
    ae_int_t fkind;
    ae_int_t ckind;
    ae_int_t fk;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&vd, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&units, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _minbleicstate_init(&state, _state, ae_true);
    _minbleicreport_init(&rep, _state, ae_true);

    
    /*
     * Preconditioner test 1.
     *
     * If
     * * B1 is default preconditioner with unit scale
     * * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
     * * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * then B1 is worse than G1, B2 is worse than G2.
     * "Worse" means more iterations to converge.
     *
     * Test problem setup:
     * * f(x) = sum( ((i*i+1)^FK*x[i])^2, i=0..N-1)
     * * FK is either +1 or -1 (we try both to test different aspects of preconditioning)
     * * constraints:
     *   0) absent
     *   1) boundary only
     *   2) linear equality only
     *   3) combination of boundary and linear equality constraints
     *
     * N        - problem size
     * K        - number of repeated passes (should be large enough to average out random factors)
     */
    k = 30;
    epsg = 1.0E-10;
    for(n=5; n<=8; n++)
    {
        for(fkind=0; fkind<=1; fkind++)
        {
            for(ckind=0; ckind<=3; ckind++)
            {
                fk = 1-2*fkind;
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&units, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 0;
                    units.ptr.p_double[i] = 1;
                }
                minbleiccreate(n, &x, &state, _state);
                if( ckind==1||ckind==3 )
                {
                    ae_vector_set_length(&bl, n, _state);
                    ae_vector_set_length(&bu, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        bl.ptr.p_double[i] = -1;
                        bu.ptr.p_double[i] = 1;
                    }
                    minbleicsetbc(&state, &bl, &bu, _state);
                }
                if( ckind==2||ckind==3 )
                {
                    ae_matrix_set_length(&c, 1, n+1, _state);
                    ae_vector_set_length(&ct, 1, _state);
                    ct.ptr.p_int[0] = ae_randominteger(3, _state)-1;
                    for(i=0; i<=n-1; i++)
                    {
                        c.ptr.pp_double[0][i] = 2*ae_randomreal(_state)-1;
                    }
                    c.ptr.pp_double[0][n] = 0;
                    minbleicsetlc(&state, &c, &ct, 1, _state);
                }
                
                /*
                 * Test it with default preconditioner VS. perturbed diagonal preconditioner
                 */
                minbleicsetprecdefault(&state, _state);
                minbleicsetscale(&state, &units, _state);
                cntb1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    minbleicrestartfrom(&state, &x, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        testminbleicunit_calciip2(&state, n, fk, _state);
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    cntb1 = cntb1+rep.inneriterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                ae_vector_set_length(&diagh, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    diagh.ptr.p_double[i] = 2*ae_pow(i*i+1, 2*fk, _state)*(0.8+0.4*ae_randomreal(_state));
                }
                minbleicsetprecdiag(&state, &diagh, _state);
                minbleicsetscale(&state, &units, _state);
                cntg1 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    minbleicrestartfrom(&state, &x, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        testminbleicunit_calciip2(&state, n, fk, _state);
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    cntg1 = cntg1+rep.inneriterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                *err = *err||cntb1<cntg1;
                
                /*
                 * Test it with scale-based preconditioner
                 */
                ae_vector_set_length(&s, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    s.ptr.p_double[i] = 1/ae_sqrt(2*ae_pow(i*i+1, 2*fk, _state)*(0.8+0.4*ae_randomreal(_state)), _state);
                }
                minbleicsetprecdefault(&state, _state);
                minbleicsetscale(&state, &s, _state);
                cntb2 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    minbleicrestartfrom(&state, &x, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        testminbleicunit_calciip2(&state, n, fk, _state);
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    cntb2 = cntb2+rep.inneriterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                minbleicsetprecscale(&state, _state);
                minbleicsetscale(&state, &s, _state);
                cntg2 = 0;
                for(pass=0; pass<=k-1; pass++)
                {
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    minbleicrestartfrom(&state, &x, _state);
                    while(minbleiciteration(&state, _state))
                    {
                        testminbleicunit_calciip2(&state, n, fk, _state);
                    }
                    minbleicresults(&state, &x, &rep, _state);
                    cntg2 = cntg2+rep.inneriterationscount;
                    *err = *err||rep.terminationtype<=0;
                }
                *err = *err||cntb2<cntg2;
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function sets random preconditioner:
* unit one, for PrecKind=0
* diagonal-based one, for PrecKind=1
* scale-based one, for PrecKind=2
*************************************************************************/
static void testminbleicunit_setrandompreconditioner(minbleicstate* state,
     ae_int_t n,
     ae_int_t preckind,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector p;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&p, 0, DT_REAL, _state, ae_true);

    if( preckind==1 )
    {
        ae_vector_set_length(&p, n, _state);
        for(i=0; i<=n-1; i++)
        {
            p.ptr.p_double[i] = ae_exp(10*ae_randomreal(_state)-5, _state);
        }
        minbleicsetprecdiag(state, &p, _state);
    }
    else
    {
        minbleicsetprecdefault(state, _state);
    }
    ae_frame_leave(_state);
}



static void testmcpdunit_testsimple(ae_bool* err, ae_state *_state);
static void testmcpdunit_testentryexit(ae_bool* err, ae_state *_state);
static void testmcpdunit_testec(ae_bool* err, ae_state *_state);
static void testmcpdunit_testbc(ae_bool* err, ae_state *_state);
static void testmcpdunit_testlc(ae_bool* err, ae_state *_state);
static void testmcpdunit_createee(ae_int_t n,
     ae_int_t entrystate,
     ae_int_t exitstate,
     mcpdstate* s,
     ae_state *_state);





ae_bool testmcpd(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool simpleerrors;
    ae_bool entryexiterrors;
    ae_bool ecerrors;
    ae_bool bcerrors;
    ae_bool lcerrors;
    ae_bool othererrors;
    ae_bool result;


    
    /*
     * Init
     */
    waserrors = ae_false;
    othererrors = ae_false;
    simpleerrors = ae_false;
    entryexiterrors = ae_false;
    ecerrors = ae_false;
    bcerrors = ae_false;
    lcerrors = ae_false;
    
    /*
     * Test
     */
    testmcpdunit_testsimple(&simpleerrors, _state);
    testmcpdunit_testentryexit(&entryexiterrors, _state);
    testmcpdunit_testec(&ecerrors, _state);
    testmcpdunit_testbc(&bcerrors, _state);
    testmcpdunit_testlc(&lcerrors, _state);
    
    /*
     * Final report
     */
    waserrors = ((((othererrors||simpleerrors)||entryexiterrors)||ecerrors)||bcerrors)||lcerrors;
    if( !silent )
    {
        printf("MCPD TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* SIMPLE:                                ");
        if( !simpleerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* ENTRY/EXIT:                            ");
        if( !entryexiterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* EQUALITY CONSTRAINTS:                  ");
        if( !ecerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* BOUND CONSTRAINTS:                     ");
        if( !bcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* LINEAR CONSTRAINTS:                    ");
        if( !lcerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OTHER PROPERTIES:                      ");
        if( !othererrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    return result;
}


/*************************************************************************
Simple test with no "entry"/"exit" states

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testsimple(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix pexact;
    ae_matrix xy;
    double threshold;
    ae_int_t i;
    ae_int_t j;
    double v;
    double v0;
    ae_matrix p;
    mcpdstate s;
    mcpdreport rep;
    double offdiagonal;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&pexact, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    threshold = 1.0E-2;
    
    /*
     * First test:
     * * N-dimensional problem
     * * proportional data
     * * no "entry"/"exit" states
     * * N tracks, each includes only two states
     * * first record in I-th track is [0 ... 1 ... 0] with 1 is in I-th position
     * * all tracks are modelled using randomly generated transition matrix P
     */
    for(n=1; n<=5; n++)
    {
        
        /*
         * Initialize "exact" P:
         * * fill by random values
         * * make sure that each column sums to non-zero value
         * * normalize
         */
        ae_matrix_set_length(&pexact, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                pexact.ptr.pp_double[i][j] = ae_randomreal(_state);
            }
        }
        for(j=0; j<=n-1; j++)
        {
            i = ae_randominteger(n, _state);
            pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]+0.1;
        }
        for(j=0; j<=n-1; j++)
        {
            v = 0;
            for(i=0; i<=n-1; i++)
            {
                v = v+pexact.ptr.pp_double[i][j];
            }
            for(i=0; i<=n-1; i++)
            {
                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
            }
        }
        
        /*
         * Initialize solver:
         * * create object
         * * add tracks
         */
        mcpdcreate(n, &s, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_matrix_set_length(&xy, 2, n, _state);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[0][j] = 0;
            }
            xy.ptr.pp_double[0][i] = 1;
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[1][j] = pexact.ptr.pp_double[j][i];
            }
            mcpdaddtrack(&s, &xy, 2, _state);
        }
        
        /*
         * Solve and test
         */
        mcpdsolve(&s, _state);
        mcpdresults(&s, &p, &rep, _state);
        if( rep.terminationtype>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
        else
        {
            *err = ae_true;
        }
    }
    
    /*
     * Second test:
     * * N-dimensional problem
     * * proportional data
     * * no "entry"/"exit" states
     * * N tracks, each includes only two states
     * * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position
     * * all tracks are modelled using randomly generated transition matrix P
     */
    offdiagonal = 0.1;
    for(n=1; n<=5; n++)
    {
        
        /*
         * Initialize "exact" P:
         * * fill by random values
         * * make sure that each column sums to non-zero value
         * * normalize
         */
        ae_matrix_set_length(&pexact, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                pexact.ptr.pp_double[i][j] = ae_randomreal(_state);
            }
        }
        for(j=0; j<=n-1; j++)
        {
            i = ae_randominteger(n, _state);
            pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]+0.1;
        }
        for(j=0; j<=n-1; j++)
        {
            v = 0;
            for(i=0; i<=n-1; i++)
            {
                v = v+pexact.ptr.pp_double[i][j];
            }
            for(i=0; i<=n-1; i++)
            {
                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
            }
        }
        
        /*
         * Initialize solver:
         * * create object
         * * add tracks
         */
        mcpdcreate(n, &s, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_matrix_set_length(&xy, 2, n, _state);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[0][j] = 0;
            }
            
            /*
             * "main" element
             */
            xy.ptr.pp_double[0][i] = 1.0-2*offdiagonal;
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[1][j] = (1.0-2*offdiagonal)*pexact.ptr.pp_double[j][i];
            }
            
            /*
             * off-diagonal ones
             */
            if( i>0 )
            {
                xy.ptr.pp_double[0][i-1] = offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+offdiagonal*pexact.ptr.pp_double[j][i-1];
                }
            }
            if( i<n-1 )
            {
                xy.ptr.pp_double[0][i+1] = offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+offdiagonal*pexact.ptr.pp_double[j][i+1];
                }
            }
            mcpdaddtrack(&s, &xy, 2, _state);
        }
        
        /*
         * Solve and test
         */
        mcpdsolve(&s, _state);
        mcpdresults(&s, &p, &rep, _state);
        if( rep.terminationtype>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
        else
        {
            *err = ae_true;
        }
    }
    
    /*
     * Third test:
     * * N-dimensional problem
     * * population data
     * * no "entry"/"exit" states
     * * N tracks, each includes only two states
     * * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position, V in [1,10]
     * * all tracks are modelled using randomly generated transition matrix P
     */
    offdiagonal = 0.1;
    for(n=1; n<=5; n++)
    {
        
        /*
         * Initialize "exact" P:
         * * fill by random values
         * * make sure that each column sums to non-zero value
         * * normalize
         */
        ae_matrix_set_length(&pexact, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                pexact.ptr.pp_double[i][j] = ae_randomreal(_state);
            }
        }
        for(j=0; j<=n-1; j++)
        {
            i = ae_randominteger(n, _state);
            pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]+0.1;
        }
        for(j=0; j<=n-1; j++)
        {
            v = 0;
            for(i=0; i<=n-1; i++)
            {
                v = v+pexact.ptr.pp_double[i][j];
            }
            for(i=0; i<=n-1; i++)
            {
                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
            }
        }
        
        /*
         * Initialize solver:
         * * create object
         * * add tracks
         */
        mcpdcreate(n, &s, _state);
        for(i=0; i<=n-1; i++)
        {
            ae_matrix_set_length(&xy, 2, n, _state);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[0][j] = 0;
            }
            
            /*
             * "main" element
             */
            v0 = 9*ae_randomreal(_state)+1;
            xy.ptr.pp_double[0][i] = v0*(1.0-2*offdiagonal);
            for(j=0; j<=n-1; j++)
            {
                xy.ptr.pp_double[1][j] = v0*(1.0-2*offdiagonal)*pexact.ptr.pp_double[j][i];
            }
            
            /*
             * off-diagonal ones
             */
            if( i>0 )
            {
                xy.ptr.pp_double[0][i-1] = v0*offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+v0*offdiagonal*pexact.ptr.pp_double[j][i-1];
                }
            }
            if( i<n-1 )
            {
                xy.ptr.pp_double[0][i+1] = v0*offdiagonal;
                for(j=0; j<=n-1; j++)
                {
                    xy.ptr.pp_double[1][j] = xy.ptr.pp_double[1][j]+v0*offdiagonal*pexact.ptr.pp_double[j][i+1];
                }
            }
            mcpdaddtrack(&s, &xy, 2, _state);
        }
        
        /*
         * Solve and test
         */
        mcpdsolve(&s, _state);
        mcpdresults(&s, &p, &rep, _state);
        if( rep.terminationtype>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                }
            }
        }
        else
        {
            *err = ae_true;
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test for different combinations of "entry"/"exit" models

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testentryexit(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix pexact;
    ae_matrix xy;
    double threshold;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t popkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&pexact, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    threshold = 1.0E-3;
    
    /*
     *
     */
    for(n=2; n<=5; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                for(popkind=0; popkind<=1; popkind++)
                {
                    
                    /*
                     * Generate EntryState/ExitState such that one of the following is True:
                     * * EntryState<>ExitState
                     * * EntryState=-1 or ExitState=-1
                     */
                    do
                    {
                        if( entrykind==0 )
                        {
                            entrystate = -1;
                        }
                        else
                        {
                            entrystate = ae_randominteger(n, _state);
                        }
                        if( exitkind==0 )
                        {
                            exitstate = -1;
                        }
                        else
                        {
                            exitstate = ae_randominteger(n, _state);
                        }
                    }
                    while(!((entrystate==-1||exitstate==-1)||entrystate!=exitstate));
                    
                    /*
                     * Generate transition matrix P such that:
                     * * columns corresponding to non-exit states sums to 1.0
                     * * columns corresponding to exit states sums to 0.0
                     * * rows corresponding to entry states are zero
                     */
                    ae_matrix_set_length(&pexact, n, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            pexact.ptr.pp_double[i][j] = 1+ae_randominteger(5, _state);
                            if( i==entrystate )
                            {
                                pexact.ptr.pp_double[i][j] = 0.0;
                            }
                            if( j==exitstate )
                            {
                                pexact.ptr.pp_double[i][j] = 0.0;
                            }
                        }
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        v = 0.0;
                        for(i=0; i<=n-1; i++)
                        {
                            v = v+pexact.ptr.pp_double[i][j];
                        }
                        if( ae_fp_neq(v,0) )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                pexact.ptr.pp_double[i][j] = pexact.ptr.pp_double[i][j]/v;
                            }
                        }
                    }
                    
                    /*
                     * Create MCPD solver
                     */
                    if( entrystate<0&&exitstate<0 )
                    {
                        mcpdcreate(n, &s, _state);
                    }
                    if( entrystate>=0&&exitstate<0 )
                    {
                        mcpdcreateentry(n, entrystate, &s, _state);
                    }
                    if( entrystate<0&&exitstate>=0 )
                    {
                        mcpdcreateexit(n, exitstate, &s, _state);
                    }
                    if( entrystate>=0&&exitstate>=0 )
                    {
                        mcpdcreateentryexit(n, entrystate, exitstate, &s, _state);
                    }
                    
                    /*
                     * Add N tracks.
                     *
                     * K-th track starts from vector with large value of
                     * K-th component and small random noise in other components.
                     *
                     * Track contains from 2 to 4 elements.
                     *
                     * Tracks contain proportional (normalized) or
                     * population data, depending on PopKind variable.
                     */
                    for(k=0; k<=n-1; k++)
                    {
                        
                        /*
                         * Generate track whose length is in 2..4
                         */
                        ae_matrix_set_length(&xy, 2+ae_randominteger(3, _state), n, _state);
                        for(j=0; j<=n-1; j++)
                        {
                            xy.ptr.pp_double[0][j] = 0.05*ae_randomreal(_state);
                        }
                        xy.ptr.pp_double[0][k] = 1+ae_randomreal(_state);
                        for(i=1; i<=xy.rows-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( j!=entrystate )
                                {
                                    v = ae_v_dotproduct(&pexact.ptr.pp_double[j][0], 1, &xy.ptr.pp_double[i-1][0], 1, ae_v_len(0,n-1));
                                    xy.ptr.pp_double[i][j] = v;
                                }
                                else
                                {
                                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                                }
                            }
                        }
                        
                        /*
                         * Normalize, if needed
                         */
                        if( popkind==1 )
                        {
                            for(i=0; i<=xy.rows-1; i++)
                            {
                                v = 0.0;
                                for(j=0; j<=n-1; j++)
                                {
                                    v = v+xy.ptr.pp_double[i][j];
                                }
                                if( ae_fp_greater(v,0) )
                                {
                                    for(j=0; j<=n-1; j++)
                                    {
                                        xy.ptr.pp_double[i][j] = xy.ptr.pp_double[i][j]/v;
                                    }
                                }
                            }
                        }
                        
                        /*
                         * Add track
                         */
                        mcpdaddtrack(&s, &xy, xy.rows, _state);
                    }
                    
                    /*
                     * Solve and test
                     */
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    if( rep.terminationtype>0 )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                *err = *err||ae_fp_greater(ae_fabs(p.ptr.pp_double[i][j]-pexact.ptr.pp_double[i][j], _state),threshold);
                            }
                        }
                    }
                    else
                    {
                        *err = ae_true;
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test equality constraints.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testec(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix ec;
    ae_matrix xy;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ic;
    ae_int_t jc;
    double vc;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ec, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    
    /*
     * We try different problems with following properties:
     * * N is large enough - we won't have problems with inconsistent constraints
     * * first state is either "entry" or "normal"
     * * last state is either "exit" or "normal"
     * * we have one long random track
     *
     * We test several properties which are described in comments below
     */
    for(n=4; n<=6; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                
                /*
                 * Prepare problem
                 */
                if( entrykind==0 )
                {
                    entrystate = -1;
                }
                else
                {
                    entrystate = 0;
                }
                if( exitkind==0 )
                {
                    exitstate = -1;
                }
                else
                {
                    exitstate = n-1;
                }
                ae_matrix_set_length(&xy, 2*n, n, _state);
                for(i=0; i<=xy.rows-1; i++)
                {
                    for(j=0; j<=xy.cols-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                    }
                }
                
                /*
                 * Test that single equality constraint on non-entry
                 * non-exit elements of P is satisfied.
                 *
                 * NOTE: this test needs N>=4 because smaller values
                 * can give us inconsistent constraints
                 */
                ae_assert(n>=4, "TestEC: expectation failed", _state);
                ic = 1+ae_randominteger(n-2, _state);
                jc = 1+ae_randominteger(n-2, _state);
                vc = ae_randomreal(_state);
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdaddec(&s, ic, jc, vc, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    *err = *err||ae_fp_neq(p.ptr.pp_double[ic][jc],vc);
                }
                else
                {
                    *err = ae_true;
                }
                
                /*
                 * Test interaction with default "sum-to-one" constraint
                 * on columns of P.
                 *
                 * We set N-1 equality constraints on random non-exit column
                 * of P, which are inconsistent with this default constraint
                 * (sum will be greater that 1.0).
                 *
                 * Algorithm must detect inconsistency.
                 *
                 * NOTE:
                 * 1. we do not set constraints for the first element of
                 *    the column, because this element may be constrained by
                 *    "exit state" constraint.
                 * 2. this test needs N>=3
                 */
                ae_assert(n>=3, "TestEC: expectation failed", _state);
                jc = ae_randominteger(n-1, _state);
                vc = 0.95;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                for(i=1; i<=n-1; i++)
                {
                    mcpdaddec(&s, i, jc, vc, _state);
                }
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype!=-3;
                
                /*
                 * Test interaction with constrains on entry states.
                 *
                 * When model has entry state, corresponding row of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this row:
                 * * zero equality constraint, which must be consistent
                 * * non-zero equality constraint, which must be inconsistent
                 */
                if( entrystate>=0 )
                {
                    jc = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, entrystate, jc, 0.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, entrystate, jc, 0.5, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test interaction with constrains on exit states.
                 *
                 * When model has exit state, corresponding column of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this column:
                 * * zero equality constraint, which must be consistent
                 * * non-zero equality constraint, which must be inconsistent
                 */
                if( exitstate>=0 )
                {
                    ic = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, ic, exitstate, 0.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddec(&s, ic, exitstate, 0.5, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test SetEC() call - we constrain subset of non-entry
                 * non-exit elements and test it.
                 */
                ae_assert(n>=4, "TestEC: expectation failed", _state);
                ae_matrix_set_length(&ec, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        ec.ptr.pp_double[i][j] = _state->v_nan;
                    }
                }
                for(j=1; j<=n-2; j++)
                {
                    ec.ptr.pp_double[1+ae_randominteger(n-2, _state)][j] = 0.1+0.1*ae_randomreal(_state);
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetec(&s, &ec, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( ae_isfinite(ec.ptr.pp_double[i][j], _state) )
                            {
                                *err = *err||ae_fp_neq(p.ptr.pp_double[i][j],ec.ptr.pp_double[i][j]);
                            }
                        }
                    }
                }
                else
                {
                    *err = ae_true;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test bound constraints.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testbc(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix bndl;
    ae_matrix bndu;
    ae_matrix xy;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ic;
    ae_int_t jc;
    double vl;
    double vu;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bndl, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bndu, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    
    /*
     * We try different problems with following properties:
     * * N is large enough - we won't have problems with inconsistent constraints
     * * first state is either "entry" or "normal"
     * * last state is either "exit" or "normal"
     * * we have one long random track
     *
     * We test several properties which are described in comments below
     */
    for(n=4; n<=6; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                
                /*
                 * Prepare problem
                 */
                if( entrykind==0 )
                {
                    entrystate = -1;
                }
                else
                {
                    entrystate = 0;
                }
                if( exitkind==0 )
                {
                    exitstate = -1;
                }
                else
                {
                    exitstate = n-1;
                }
                ae_matrix_set_length(&xy, 2*n, n, _state);
                for(i=0; i<=xy.rows-1; i++)
                {
                    for(j=0; j<=xy.cols-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                    }
                }
                
                /*
                 * Test that single bound constraint on non-entry
                 * non-exit elements of P is satisfied.
                 *
                 * NOTE 1: this test needs N>=4 because smaller values
                 * can give us inconsistent constraints
                 */
                ae_assert(n>=4, "TestBC: expectation failed", _state);
                ic = 1+ae_randominteger(n-2, _state);
                jc = 1+ae_randominteger(n-2, _state);
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    vl = 0.3*ae_randomreal(_state);
                }
                else
                {
                    vl = _state->v_neginf;
                }
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    vu = 0.5+0.3*ae_randomreal(_state);
                }
                else
                {
                    vu = _state->v_posinf;
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdaddbc(&s, ic, jc, vl, vu, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    *err = *err||ae_fp_less(p.ptr.pp_double[ic][jc],vl);
                    *err = *err||ae_fp_greater(p.ptr.pp_double[ic][jc],vu);
                }
                else
                {
                    *err = ae_true;
                }
                
                /*
                 * Test interaction with default "sum-to-one" constraint
                 * on columns of P.
                 *
                 * We set N-1 bound constraints on random non-exit column
                 * of P, which are inconsistent with this default constraint
                 * (sum will be greater that 1.0).
                 *
                 * Algorithm must detect inconsistency.
                 *
                 * NOTE:
                 * 1. we do not set constraints for the first element of
                 *    the column, because this element may be constrained by
                 *    "exit state" constraint.
                 * 2. this test needs N>=3
                 */
                ae_assert(n>=3, "TestEC: expectation failed", _state);
                jc = ae_randominteger(n-1, _state);
                vl = 0.85;
                vu = 0.95;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                for(i=1; i<=n-1; i++)
                {
                    mcpdaddbc(&s, i, jc, vl, vu, _state);
                }
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype!=-3;
                
                /*
                 * Test interaction with constrains on entry states.
                 *
                 * When model has entry state, corresponding row of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this row:
                 * * bound constraint with zero lower bound, which must be consistent
                 * * bound constraint with non-zero lower bound, which must be inconsistent
                 */
                if( entrystate>=0 )
                {
                    jc = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, entrystate, jc, 0.0, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, entrystate, jc, 0.5, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test interaction with constrains on exit states.
                 *
                 * When model has exit state, corresponding column of P
                 * must be zero. We try to set two kinds of constraints
                 * on random element of this column:
                 * * bound constraint with zero lower bound, which must be consistent
                 * * bound constraint with non-zero lower bound, which must be inconsistent
                 */
                if( exitstate>=0 )
                {
                    ic = ae_randominteger(n, _state);
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, ic, exitstate, 0.0, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdaddbc(&s, ic, exitstate, 0.5, 1.0, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test SetBC() call - we constrain subset of non-entry
                 * non-exit elements and test it.
                 */
                ae_assert(n>=4, "TestBC: expectation failed", _state);
                ae_matrix_set_length(&bndl, n, n, _state);
                ae_matrix_set_length(&bndu, n, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        bndl.ptr.pp_double[i][j] = _state->v_neginf;
                        bndu.ptr.pp_double[i][j] = _state->v_posinf;
                    }
                }
                for(j=1; j<=n-2; j++)
                {
                    i = 1+ae_randominteger(n-2, _state);
                    bndl.ptr.pp_double[i][j] = 0.5-0.1*ae_randomreal(_state);
                    bndu.ptr.pp_double[i][j] = 0.5+0.1*ae_randomreal(_state);
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetbc(&s, &bndl, &bndu, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            *err = *err||ae_fp_less(p.ptr.pp_double[i][j],bndl.ptr.pp_double[i][j]);
                            *err = *err||ae_fp_greater(p.ptr.pp_double[i][j],bndu.ptr.pp_double[i][j]);
                        }
                    }
                }
                else
                {
                    *err = ae_true;
                }
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Test bound constraints.

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testmcpdunit_testlc(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_matrix p;
    ae_matrix c;
    ae_matrix xy;
    ae_vector ct;
    ae_int_t entrystate;
    ae_int_t exitstate;
    ae_int_t entrykind;
    ae_int_t exitkind;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t t;
    ae_int_t jc;
    double v;
    double threshold;
    mcpdstate s;
    mcpdreport rep;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&p, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
    _mcpdstate_init(&s, _state, ae_true);
    _mcpdreport_init(&rep, _state, ae_true);

    threshold = 1.0E5*ae_machineepsilon;
    
    /*
     * We try different problems with following properties:
     * * N is large enough - we won't have problems with inconsistent constraints
     * * first state is either "entry" or "normal"
     * * last state is either "exit" or "normal"
     * * we have one long random track
     *
     * We test several properties which are described in comments below
     */
    for(n=4; n<=6; n++)
    {
        for(entrykind=0; entrykind<=1; entrykind++)
        {
            for(exitkind=0; exitkind<=1; exitkind++)
            {
                
                /*
                 * Prepare problem
                 */
                if( entrykind==0 )
                {
                    entrystate = -1;
                }
                else
                {
                    entrystate = 0;
                }
                if( exitkind==0 )
                {
                    exitstate = -1;
                }
                else
                {
                    exitstate = n-1;
                }
                ae_matrix_set_length(&xy, 2*n, n, _state);
                for(i=0; i<=xy.rows-1; i++)
                {
                    for(j=0; j<=xy.cols-1; j++)
                    {
                        xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                    }
                }
                
                /*
                 * Test that single linear equality/inequality constraint
                 * on non-entry non-exit elements of P is satisfied.
                 *
                 * NOTE 1: this test needs N>=4 because smaller values
                 *         can give us inconsistent constraints
                 * NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N)
                 *         is always feasible. It guarantees that there always exists
                 *         at least one feasible point
                 * NOTE 3: If we have inequality constraint, we "shift" right part
                 *         in order to make feasible some neighborhood of P=(1/N ... 1/N).
                 */
                ae_assert(n>=4, "TestLC: expectation failed", _state);
                ae_matrix_set_length(&c, 1, n*n+1, _state);
                ae_vector_set_length(&ct, 1, _state);
                v = 0;
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        if( ((i==0||i==n-1)||j==0)||j==n-1 )
                        {
                            c.ptr.pp_double[0][i*n+j] = 0;
                        }
                        else
                        {
                            c.ptr.pp_double[0][i*n+j] = ae_randomreal(_state);
                            v = v+c.ptr.pp_double[0][i*n+j]*((double)1/(double)n);
                        }
                    }
                }
                c.ptr.pp_double[0][n*n] = v;
                ct.ptr.p_int[0] = ae_randominteger(3, _state)-1;
                if( ct.ptr.p_int[0]<0 )
                {
                    c.ptr.pp_double[0][n*n] = c.ptr.pp_double[0][n*n]+0.1;
                }
                if( ct.ptr.p_int[0]>0 )
                {
                    c.ptr.pp_double[0][n*n] = c.ptr.pp_double[0][n*n]-0.1;
                }
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetlc(&s, &c, &ct, 1, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                if( rep.terminationtype>0 )
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+p.ptr.pp_double[i][j]*c.ptr.pp_double[0][i*n+j];
                        }
                    }
                    if( ct.ptr.p_int[0]<0 )
                    {
                        *err = *err||ae_fp_greater_eq(v,c.ptr.pp_double[0][n*n]+threshold);
                    }
                    if( ct.ptr.p_int[0]==0 )
                    {
                        *err = *err||ae_fp_greater_eq(ae_fabs(v-c.ptr.pp_double[0][n*n], _state),threshold);
                    }
                    if( ct.ptr.p_int[0]>0 )
                    {
                        *err = *err||ae_fp_less_eq(v,c.ptr.pp_double[0][n*n]-threshold);
                    }
                }
                else
                {
                    *err = ae_true;
                }
                
                /*
                 * Test interaction with default "sum-to-one" constraint
                 * on columns of P.
                 *
                 * We set linear constraint which has for "sum-to-X" on
                 * on random non-exit column of P. This constraint can be
                 * either consistent (X=1.0) or inconsistent (X<>1.0) with
                 * this default constraint.
                 *
                 * Algorithm must detect inconsistency.
                 *
                 * NOTE:
                 * 1. this test needs N>=2
                 */
                ae_assert(n>=2, "TestLC: expectation failed", _state);
                jc = ae_randominteger(n-1, _state);
                ae_matrix_set_length(&c, 1, n*n+1, _state);
                ae_vector_set_length(&ct, 1, _state);
                for(i=0; i<=n*n-1; i++)
                {
                    c.ptr.pp_double[0][i] = 0.0;
                }
                for(i=0; i<=n-1; i++)
                {
                    c.ptr.pp_double[0][n*i+jc] = 1.0;
                }
                c.ptr.pp_double[0][n*n] = 1.0;
                ct.ptr.p_int[0] = 0;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetlc(&s, &c, &ct, 1, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype<=0;
                c.ptr.pp_double[0][n*n] = 2.0;
                testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                mcpdaddtrack(&s, &xy, xy.rows, _state);
                mcpdsetlc(&s, &c, &ct, 1, _state);
                mcpdsolve(&s, _state);
                mcpdresults(&s, &p, &rep, _state);
                *err = *err||rep.terminationtype!=-3;
                
                /*
                 * Test interaction with constrains on entry states.
                 *
                 * When model has entry state, corresponding row of P
                 * must be zero. We try to set two kinds of constraints
                 * on elements of this row:
                 * * sums-to-zero constraint, which must be consistent
                 * * sums-to-one constraint, which must be inconsistent
                 */
                if( entrystate>=0 )
                {
                    ae_matrix_set_length(&c, 1, n*n+1, _state);
                    ae_vector_set_length(&ct, 1, _state);
                    for(i=0; i<=n*n-1; i++)
                    {
                        c.ptr.pp_double[0][i] = 0.0;
                    }
                    for(j=0; j<=n-1; j++)
                    {
                        c.ptr.pp_double[0][n*entrystate+j] = 1.0;
                    }
                    ct.ptr.p_int[0] = 0;
                    c.ptr.pp_double[0][n*n] = 0.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    c.ptr.pp_double[0][n*n] = 1.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
                
                /*
                 * Test interaction with constrains on exit states.
                 *
                 * When model has exit state, corresponding column of P
                 * must be zero. We try to set two kinds of constraints
                 * on elements of this column:
                 * * sums-to-zero constraint, which must be consistent
                 * * sums-to-one constraint, which must be inconsistent
                 */
                if( exitstate>=0 )
                {
                    ae_matrix_set_length(&c, 1, n*n+1, _state);
                    ae_vector_set_length(&ct, 1, _state);
                    for(i=0; i<=n*n-1; i++)
                    {
                        c.ptr.pp_double[0][i] = 0.0;
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        c.ptr.pp_double[0][n*i+exitstate] = 1.0;
                    }
                    ct.ptr.p_int[0] = 0;
                    c.ptr.pp_double[0][n*n] = 0.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype<=0;
                    c.ptr.pp_double[0][n*n] = 1.0;
                    testmcpdunit_createee(n, entrystate, exitstate, &s, _state);
                    mcpdaddtrack(&s, &xy, xy.rows, _state);
                    mcpdsetlc(&s, &c, &ct, 1, _state);
                    mcpdsolve(&s, _state);
                    mcpdresults(&s, &p, &rep, _state);
                    *err = *err||rep.terminationtype!=-3;
                }
            }
        }
    }
    
    /*
     * Final test - we generate several random constraints and
     * test SetLC() function.
     *
     * NOTES:
     *
     * 1. Constraints are generated is such a way that P=(1/N ... 1/N)
     *    is always feasible. It guarantees that there always exists
     *    at least one feasible point
     * 2. For simplicity of the test we do not use entry/exit states
     *    in our model
     */
    for(n=1; n<=4; n++)
    {
        for(k=1; k<=2*n; k++)
        {
            
            /*
             * Generate track
             */
            ae_matrix_set_length(&xy, 2*n, n, _state);
            for(i=0; i<=xy.rows-1; i++)
            {
                for(j=0; j<=xy.cols-1; j++)
                {
                    xy.ptr.pp_double[i][j] = ae_randomreal(_state);
                }
            }
            
            /*
             * Generate random constraints
             */
            ae_matrix_set_length(&c, k, n*n+1, _state);
            ae_vector_set_length(&ct, k, _state);
            for(i=0; i<=k-1; i++)
            {
                
                /*
                 * Generate constraint and its right part
                 */
                c.ptr.pp_double[i][n*n] = 0;
                for(j=0; j<=n*n-1; j++)
                {
                    c.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                    c.ptr.pp_double[i][n*n] = c.ptr.pp_double[i][n*n]+c.ptr.pp_double[i][j]*((double)1/(double)n);
                }
                ct.ptr.p_int[i] = ae_randominteger(3, _state)-1;
                
                /*
                 * If we have inequality constraint, we "shift" right part
                 * in order to make feasible some neighborhood of P=(1/N ... 1/N).
                 */
                if( ct.ptr.p_int[i]<0 )
                {
                    c.ptr.pp_double[i][n*n] = c.ptr.pp_double[i][n*n]+0.1;
                }
                if( ct.ptr.p_int[i]>0 )
                {
                    c.ptr.pp_double[i][n*n] = c.ptr.pp_double[i][n*n]-0.1;
                }
            }
            
            /*
             * Test
             */
            testmcpdunit_createee(n, -1, -1, &s, _state);
            mcpdaddtrack(&s, &xy, xy.rows, _state);
            mcpdsetlc(&s, &c, &ct, k, _state);
            mcpdsolve(&s, _state);
            mcpdresults(&s, &p, &rep, _state);
            if( rep.terminationtype>0 )
            {
                for(t=0; t<=k-1; t++)
                {
                    v = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            v = v+p.ptr.pp_double[i][j]*c.ptr.pp_double[t][i*n+j];
                        }
                    }
                    if( ct.ptr.p_int[t]<0 )
                    {
                        *err = *err||ae_fp_greater_eq(v,c.ptr.pp_double[t][n*n]+threshold);
                    }
                    if( ct.ptr.p_int[t]==0 )
                    {
                        *err = *err||ae_fp_greater_eq(ae_fabs(v-c.ptr.pp_double[t][n*n], _state),threshold);
                    }
                    if( ct.ptr.p_int[t]>0 )
                    {
                        *err = *err||ae_fp_less_eq(v,c.ptr.pp_double[t][n*n]-threshold);
                    }
                }
            }
            else
            {
                *err = ae_true;
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function is used to create MCPD object with arbitrary combination of
entry and exit states
*************************************************************************/
static void testmcpdunit_createee(ae_int_t n,
     ae_int_t entrystate,
     ae_int_t exitstate,
     mcpdstate* s,
     ae_state *_state)
{

    _mcpdstate_clear(s);

    if( entrystate<0&&exitstate<0 )
    {
        mcpdcreate(n, s, _state);
    }
    if( entrystate>=0&&exitstate<0 )
    {
        mcpdcreateentry(n, entrystate, s, _state);
    }
    if( entrystate<0&&exitstate>=0 )
    {
        mcpdcreateexit(n, exitstate, s, _state);
    }
    if( entrystate>=0&&exitstate>=0 )
    {
        mcpdcreateentryexit(n, entrystate, exitstate, s, _state);
    }
}








/*************************************************************************
Testing
*************************************************************************/
ae_bool testfbls(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_int_t mx;
    ae_int_t i;
    ae_int_t j;
    ae_bool waserrors;
    ae_bool cgerrors;
    double v;
    double v1;
    double v2;
    ae_vector tmp1;
    ae_vector tmp2;
    ae_matrix a;
    ae_vector b;
    ae_vector x;
    ae_vector xe;
    ae_vector buf;
    double alpha;
    double e1;
    double e2;
    fblslincgstate cgstate;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);
    _fblslincgstate_init(&cgstate, _state, ae_true);

    mx = 10;
    waserrors = ae_false;
    cgerrors = ae_false;
    
    /*
     * Test CG solver:
     * * generate problem (A, B, Alpha, XE - exact solution) and initial approximation X
     * * E1 = ||A'A*x-b||
     * * solve
     * * E2 = ||A'A*x-b||
     * * test that E2<0.001*E1
     */
    for(n=1; n<=mx; n++)
    {
        for(m=1; m<=mx; m++)
        {
            ae_matrix_set_length(&a, m, n, _state);
            ae_vector_set_length(&b, n, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xe, n, _state);
            ae_vector_set_length(&tmp1, m, _state);
            ae_vector_set_length(&tmp2, n, _state);
            
            /*
             * init A, alpha, B, X (initial approximation), XE (exact solution)
             * X is initialized in such way that is has no chances to be equal to XE.
             */
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            alpha = ae_randomreal(_state)+0.1;
            for(i=0; i<=n-1; i++)
            {
                b.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(2+ae_randomreal(_state));
            }
            
            /*
             * Test dense CG (which solves A'A*x=b and accepts dense A)
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(2+ae_randomreal(_state));
            }
            rmatrixmv(m, n, &a, 0, 0, 0, &x, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e1 = ae_sqrt(v, _state);
            fblssolvecgx(&a, m, n, alpha, &b, &x, &buf, _state);
            rmatrixmv(m, n, &a, 0, 0, 0, &x, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e2 = ae_sqrt(v, _state);
            cgerrors = cgerrors||ae_fp_greater(e2,0.001*e1);
            
            /*
             * Test sparse CG (which relies on reverse communication)
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = (2*ae_randominteger(2, _state)-1)*(2+ae_randomreal(_state));
            }
            rmatrixmv(m, n, &a, 0, 0, 0, &x, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e1 = ae_sqrt(v, _state);
            fblscgcreate(&x, &b, n, &cgstate, _state);
            while(fblscgiteration(&cgstate, _state))
            {
                rmatrixmv(m, n, &a, 0, 0, 0, &cgstate.x, 0, &tmp1, 0, _state);
                rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &cgstate.ax, 0, _state);
                ae_v_addd(&cgstate.ax.ptr.p_double[0], 1, &cgstate.x.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
                v1 = ae_v_dotproduct(&tmp1.ptr.p_double[0], 1, &tmp1.ptr.p_double[0], 1, ae_v_len(0,m-1));
                v2 = ae_v_dotproduct(&cgstate.x.ptr.p_double[0], 1, &cgstate.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                cgstate.xax = v1+alpha*v2;
            }
            rmatrixmv(m, n, &a, 0, 0, 0, &cgstate.xk, 0, &tmp1, 0, _state);
            rmatrixmv(n, m, &a, 0, 0, 1, &tmp1, 0, &tmp2, 0, _state);
            ae_v_addd(&tmp2.ptr.p_double[0], 1, &cgstate.xk.ptr.p_double[0], 1, ae_v_len(0,n-1), alpha);
            ae_v_sub(&tmp2.ptr.p_double[0], 1, &b.ptr.p_double[0], 1, ae_v_len(0,n-1));
            v = ae_v_dotproduct(&tmp2.ptr.p_double[0], 1, &tmp2.ptr.p_double[0], 1, ae_v_len(0,n-1));
            e2 = ae_sqrt(v, _state);
            cgerrors = cgerrors||ae_fp_greater(ae_fabs(e1-cgstate.e1, _state),100*ae_machineepsilon*e1);
            cgerrors = cgerrors||ae_fp_greater(ae_fabs(e2-cgstate.e2, _state),100*ae_machineepsilon*e1);
            cgerrors = cgerrors||ae_fp_greater(e2,0.001*e1);
        }
    }
    
    /*
     * report
     */
    waserrors = cgerrors;
    if( !silent )
    {
        printf("TESTING FBLS\n");
        printf("CG ERRORS:                               ");
        if( cgerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testminlbfgsunit_testfunc1(minlbfgsstate* state,
     ae_state *_state);
static void testminlbfgsunit_testfunc2(minlbfgsstate* state,
     ae_state *_state);
static void testminlbfgsunit_testfunc3(minlbfgsstate* state,
     ae_state *_state);
static void testminlbfgsunit_calciip2(minlbfgsstate* state,
     ae_int_t n,
     ae_state *_state);
static void testminlbfgsunit_testpreconditioning(ae_bool* err,
     ae_state *_state);
static void testminlbfgsunit_testother(ae_bool* err, ae_state *_state);





ae_bool testminlbfgs(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool referror;
    ae_bool nonconverror;
    ae_bool eqerror;
    ae_bool converror;
    ae_bool crashtest;
    ae_bool othererrors;
    ae_bool restartserror;
    ae_bool precerror;
    ae_int_t n;
    ae_int_t m;
    ae_vector x;
    ae_vector xe;
    ae_vector b;
    ae_vector xlast;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_matrix a;
    ae_vector diagh;
    ae_int_t maxits;
    minlbfgsstate state;
    minlbfgsreport rep;
    double diffstep;
    ae_int_t dkind;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);
    _minlbfgsstate_init(&state, _state, ae_true);
    _minlbfgsreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    precerror = ae_false;
    nonconverror = ae_false;
    restartserror = ae_false;
    eqerror = ae_false;
    converror = ae_false;
    crashtest = ae_false;
    othererrors = ae_false;
    testminlbfgsunit_testpreconditioning(&precerror, _state);
    testminlbfgsunit_testother(&othererrors, _state);
    
    /*
     * Reference problem
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 3, _state);
        n = 3;
        m = 2;
        x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[1] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[2] = 100*ae_randomreal(_state)-50;
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, 0, 0, 0, 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needf||state.needfg )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
            }
            if( state.needfg )
            {
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]-2)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
            }
        }
        minlbfgsresults(&state, &x, &rep, _state);
        referror = ((rep.terminationtype<=0||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-2, _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-2, _state),0.001);
    }
    
    /*
     * nonconvex problems with complex surface: we start from point with very small
     * gradient, but we need ever smaller gradient in the next step due to
     * Wolfe conditions.
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 1, _state);
        n = 1;
        m = 1;
        v = -100;
        while(ae_fp_less(v,0.1))
        {
            x.ptr.p_double[0] = v;
            if( dkind==0 )
            {
                minlbfgscreate(n, m, &x, &state, _state);
            }
            if( dkind==1 )
            {
                minlbfgscreatef(n, m, &x, diffstep, &state, _state);
            }
            minlbfgssetcond(&state, 1.0E-9, 0, 0, 0, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needf||state.needfg )
                {
                    state.f = ae_sqr(state.x.ptr.p_double[0], _state)/(1+ae_sqr(state.x.ptr.p_double[0], _state));
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[0] = (2*state.x.ptr.p_double[0]*(1+ae_sqr(state.x.ptr.p_double[0], _state))-ae_sqr(state.x.ptr.p_double[0], _state)*2*state.x.ptr.p_double[0])/ae_sqr(1+ae_sqr(state.x.ptr.p_double[0], _state), _state);
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            nonconverror = (nonconverror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0], _state),0.001);
            v = v+0.1;
        }
    }
    
    /*
     * F2 problem with restarts:
     * * make several iterations and restart BEFORE termination
     * * iterate and restart AFTER termination
     *
     * NOTE: step is bounded from above to avoid premature convergence
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 3, _state);
        n = 3;
        m = 2;
        x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetstpmax(&state, 0.1, _state);
        minlbfgssetcond(&state, 0.0000001, 0.0, 0.0, 0, _state);
        for(i=0; i<=10; i++)
        {
            if( !minlbfgsiteration(&state, _state) )
            {
                break;
            }
            testminlbfgsunit_testfunc2(&state, _state);
        }
        x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
        minlbfgsrestartfrom(&state, &x, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc2(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log(2, _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log(2, _state), _state),0.01);
        x.ptr.p_double[0] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[1] = 10+10*ae_randomreal(_state);
        x.ptr.p_double[2] = 10+10*ae_randomreal(_state);
        minlbfgsrestartfrom(&state, &x, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc2(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        restartserror = (((restartserror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log(2, _state), _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.01))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log(2, _state), _state),0.01);
    }
    
    /*
     * Linear equations
     */
    diffstep = 1.0E-6;
    for(n=1; n<=10; n++)
    {
        
        /*
         * Prepare task
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&xe, n-1+1, _state);
        ae_vector_set_length(&b, n-1+1, _state);
        for(i=0; i<=n-1; i++)
        {
            xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            a.ptr.pp_double[i][i] = a.ptr.pp_double[i][i]+3*ae_sign(a.ptr.pp_double[i][i], _state);
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b.ptr.p_double[i] = v;
        }
        
        /*
         * Test different M/DKind
         */
        for(m=1; m<=n; m++)
        {
            for(dkind=0; dkind<=1; dkind++)
            {
                
                /*
                 * Solve task
                 */
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                if( dkind==0 )
                {
                    minlbfgscreate(n, m, &x, &state, _state);
                }
                if( dkind==1 )
                {
                    minlbfgscreatef(n, m, &x, diffstep, &state, _state);
                }
                minlbfgssetcond(&state, 0, 0, 0, 0, _state);
                while(minlbfgsiteration(&state, _state))
                {
                    if( state.needf||state.needfg )
                    {
                        state.f = 0;
                    }
                    if( state.needfg )
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            state.g.ptr.p_double[i] = 0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                        if( state.needf||state.needfg )
                        {
                            state.f = state.f+ae_sqr(v-b.ptr.p_double[i], _state);
                        }
                        if( state.needfg )
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                state.g.ptr.p_double[j] = state.g.ptr.p_double[j]+2*(v-b.ptr.p_double[i])*a.ptr.pp_double[i][j];
                            }
                        }
                    }
                }
                minlbfgsresults(&state, &x, &rep, _state);
                eqerror = eqerror||rep.terminationtype<=0;
                for(i=0; i<=n-1; i++)
                {
                    eqerror = eqerror||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001);
                }
            }
        }
    }
    
    /*
     * Testing convergence properties
     */
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, 3, _state);
        n = 3;
        m = 2;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, 0.001, 0, 0, 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = converror||rep.terminationtype!=4;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, 0, 0.001, 0, 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = converror||rep.terminationtype!=1;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, 0, 0, 0.001, 0, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = converror||rep.terminationtype!=2;
        for(i=0; i<=2; i++)
        {
            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, 0, 0, 0, 10, _state);
        while(minlbfgsiteration(&state, _state))
        {
            testminlbfgsunit_testfunc3(&state, _state);
        }
        minlbfgsresults(&state, &x, &rep, _state);
        converror = (converror||rep.terminationtype!=5)||rep.iterationscount!=10;
    }
    
    /*
     * Crash test: too many iterations on a simple tasks
     * May fail when encounter zero step, underflow or something like that
     */
    ae_vector_set_length(&x, 2+1, _state);
    n = 3;
    m = 2;
    maxits = 10000;
    for(i=0; i<=2; i++)
    {
        x.ptr.p_double[i] = 6*ae_randomreal(_state)-3;
    }
    minlbfgscreate(n, m, &x, &state, _state);
    minlbfgssetcond(&state, 0, 0, 0, maxits, _state);
    while(minlbfgsiteration(&state, _state))
    {
        state.f = ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
        state.g.ptr.p_double[0] = 2*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
        state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
        state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
    }
    minlbfgsresults(&state, &x, &rep, _state);
    crashtest = crashtest||rep.terminationtype<=0;
    
    /*
     * end
     */
    waserrors = ((((((referror||nonconverror)||eqerror)||converror)||crashtest)||othererrors)||restartserror)||precerror;
    if( !silent )
    {
        printf("TESTING L-BFGS OPTIMIZATION\n");
        printf("REFERENCE PROBLEM:                        ");
        if( referror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("NON-CONVEX PROBLEM:                       ");
        if( nonconverror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LINEAR EQUATIONS:                         ");
        if( eqerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RESTARTS:                                 ");
        if( restartserror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("PRECONDITIONER:                           ");
        if( precerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CRASH TEST:                               ");
        if( crashtest )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Calculate test function #1

It may show very interesting behavior when optimized with 'x[0]>=ln(2)'
constraint.
*************************************************************************/
static void testminlbfgsunit_testfunc1(minlbfgsstate* state,
     ae_state *_state)
{


    if( ae_fp_less(state->x.ptr.p_double[0],100) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state->x.ptr.p_double[1], _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 2*state->x.ptr.p_double[1];
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = 0;
            state->g.ptr.p_double[2] = 0;
        }
    }
}


/*************************************************************************
Calculate test function #2

Simple variation of #1, much more nonlinear, which makes unlikely premature
convergence of algorithm .
*************************************************************************/
static void testminlbfgsunit_testfunc2(minlbfgsstate* state,
     ae_state *_state)
{


    if( ae_fp_less(state->x.ptr.p_double[0],100) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state), _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 4*state->x.ptr.p_double[1]*ae_sqr(state->x.ptr.p_double[1], _state);
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = 0;
            state->g.ptr.p_double[2] = 0;
        }
    }
}


/*************************************************************************
Calculate test function #3

Simple variation of #1, much more nonlinear, with non-zero value at minimum.
It achieve two goals:
* makes unlikely premature convergence of algorithm .
* solves some issues with EpsF stopping condition which arise when
  F(minimum) is zero

*************************************************************************/
static void testminlbfgsunit_testfunc3(minlbfgsstate* state,
     ae_state *_state)
{
    double s;


    s = 0.001;
    if( ae_fp_less(state->x.ptr.p_double[0],100) )
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqr(ae_exp(state->x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state->x.ptr.p_double[1], _state)+s, _state)+ae_sqr(state->x.ptr.p_double[2]-state->x.ptr.p_double[0], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = 2*(ae_exp(state->x.ptr.p_double[0], _state)-2)*ae_exp(state->x.ptr.p_double[0], _state)+2*(state->x.ptr.p_double[0]-state->x.ptr.p_double[2]);
            state->g.ptr.p_double[1] = 2*(ae_sqr(state->x.ptr.p_double[1], _state)+s)*2*state->x.ptr.p_double[1];
            state->g.ptr.p_double[2] = 2*(state->x.ptr.p_double[2]-state->x.ptr.p_double[0]);
        }
    }
    else
    {
        if( state->needf||state->needfg )
        {
            state->f = ae_sqrt(ae_maxrealnumber, _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[0] = ae_sqrt(ae_maxrealnumber, _state);
            state->g.ptr.p_double[1] = 0;
            state->g.ptr.p_double[2] = 0;
        }
    }
}


/*************************************************************************
Calculate test function IIP2

f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1)

It has high condition number which makes fast convergence unlikely without
good preconditioner.

*************************************************************************/
static void testminlbfgsunit_calciip2(minlbfgsstate* state,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;


    if( state->needf||state->needfg )
    {
        state->f = 0;
    }
    for(i=0; i<=n-1; i++)
    {
        if( state->needf||state->needfg )
        {
            state->f = state->f+ae_sqr(i*i+1, _state)*ae_sqr(state->x.ptr.p_double[i], _state);
        }
        if( state->needfg )
        {
            state->g.ptr.p_double[i] = ae_sqr(i*i+1, _state)*2*state->x.ptr.p_double[i];
        }
    }
}


/*************************************************************************
This function tests preconditioning

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminlbfgsunit_testpreconditioning(ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t cntb1;
    ae_int_t cntb2;
    ae_int_t cntg1;
    ae_int_t cntg2;
    double epsg;
    ae_int_t pkind;
    minlbfgsstate state;
    minlbfgsreport rep;
    ae_vector x;
    ae_vector s;
    ae_matrix a;
    ae_vector diagh;

    ae_frame_make(_state, &_frame_block);
    _minlbfgsstate_init(&state, _state, ae_true);
    _minlbfgsreport_init(&rep, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&diagh, 0, DT_REAL, _state, ae_true);

    m = 1;
    k = 50;
    epsg = 1.0E-10;
    
    /*
     * Preconditioner test1.
     *
     * If
     * * B1 is default preconditioner
     * * B2 is Cholesky preconditioner with unit diagonal
     * * G1 is Cholesky preconditioner based on exact Hessian with perturbations
     * * G2 is diagonal precomditioner based on approximate diagonal of Hessian matrix
     * then "bad" preconditioners (B1/B2/..) are worse than "good" ones (G1/G2/..).
     * "Worse" means more iterations to converge.
     *
     * We test it using f(x) = sum( ((i*i+1)*x[i])^2, i=0..N-1) and L-BFGS
     * optimizer with deliberately small M=1.
     *
     * N        - problem size
     * PKind    - zero for upper triangular preconditioner, one for lower triangular.
     * K        - number of repeated passes (should be large enough to average out random factors)
     */
    for(n=10; n<=15; n++)
    {
        pkind = ae_randominteger(2, _state);
        ae_vector_set_length(&x, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 0;
        }
        minlbfgscreate(n, m, &x, &state, _state);
        
        /*
         * Test it with default preconditioner
         */
        minlbfgssetprecdefault(&state, _state);
        cntb1 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntb1 = cntb1+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Test it with unit preconditioner
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    a.ptr.pp_double[i][i] = 1;
                }
                else
                {
                    a.ptr.pp_double[i][j] = 0;
                }
            }
        }
        minlbfgssetpreccholesky(&state, &a, pkind==0, _state);
        cntb2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntb2 = cntb2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Test it with perturbed Hessian preconditioner
         */
        ae_matrix_set_length(&a, n, n, _state);
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    a.ptr.pp_double[i][i] = (i*i+1)*(0.8+0.4*ae_randomreal(_state));
                }
                else
                {
                    if( (pkind==0&&j>i)||(pkind==1&&j<i) )
                    {
                        a.ptr.pp_double[i][j] = 0.1*ae_randomreal(_state)-0.05;
                    }
                    else
                    {
                        a.ptr.pp_double[i][j] = _state->v_nan;
                    }
                }
            }
        }
        minlbfgssetpreccholesky(&state, &a, pkind==0, _state);
        cntg1 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntg1 = cntg1+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Test it with perturbed diagonal preconditioner
         */
        ae_vector_set_length(&diagh, n, _state);
        for(i=0; i<=n-1; i++)
        {
            diagh.ptr.p_double[i] = 2*ae_sqr(i*i+1, _state)*(0.8+0.4*ae_randomreal(_state));
        }
        minlbfgssetprecdiag(&state, &diagh, _state);
        cntg2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntg2 = cntg2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        
        /*
         * Compare
         */
        *err = *err||cntb1<cntg1;
        *err = *err||cntb2<cntg1;
        *err = *err||cntb1<cntg2;
        *err = *err||cntb2<cntg2;
    }
    
    /*
     * Preconditioner test 2.
     *
     * If
     * * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
     * then B2 is worse than G2.
     * "Worse" means more iterations to converge.
     */
    for(n=10; n<=15; n++)
    {
        ae_vector_set_length(&x, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 0;
        }
        minlbfgscreate(n, m, &x, &state, _state);
        ae_vector_set_length(&s, n, _state);
        for(i=0; i<=n-1; i++)
        {
            s.ptr.p_double[i] = 1/ae_sqrt(2*ae_pow(i*i+1, 2, _state)*(0.8+0.4*ae_randomreal(_state)), _state);
        }
        minlbfgssetprecdefault(&state, _state);
        minlbfgssetscale(&state, &s, _state);
        cntb2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntb2 = cntb2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        minlbfgssetprecscale(&state, _state);
        minlbfgssetscale(&state, &s, _state);
        cntg2 = 0;
        for(pass=0; pass<=k-1; pass++)
        {
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                testminlbfgsunit_calciip2(&state, n, _state);
            }
            minlbfgsresults(&state, &x, &rep, _state);
            cntg2 = cntg2+rep.iterationscount;
            *err = *err||rep.terminationtype<=0;
        }
        *err = *err||cntb2<cntg2;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
This function tests other properties

On failure sets Err to True (leaves it unchanged otherwise)
*************************************************************************/
static void testminlbfgsunit_testother(ae_bool* err, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t m;
    ae_vector x;
    ae_vector a;
    ae_vector s;
    ae_vector h;
    ae_vector xlast;
    ae_bool hasxlast;
    double lastscaledstep;
    ae_int_t i;
    minlbfgsstate state;
    minlbfgsreport rep;
    double fprev;
    double xprev;
    double v;
    double stpmax;
    double tmpeps;
    double epsg;
    ae_int_t pkind;
    ae_int_t ckind;
    ae_int_t mkind;
    double vc;
    double vm;
    double diffstep;
    ae_int_t dkind;
    ae_bool wasf;
    ae_bool wasfg;
    double r;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&a, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&h, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    _minlbfgsstate_init(&state, _state, ae_true);
    _minlbfgsreport_init(&rep, _state, ae_true);

    
    /*
     * Test reports (F should form monotone sequence)
     */
    n = 50;
    m = 2;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&xlast, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = 1;
    }
    minlbfgscreate(n, m, &x, &state, _state);
    minlbfgssetcond(&state, 0, 0, 0, 100, _state);
    minlbfgssetxrep(&state, ae_true, _state);
    fprev = ae_maxrealnumber;
    while(minlbfgsiteration(&state, _state))
    {
        if( state.needfg )
        {
            state.f = 0;
            for(i=0; i<=n-1; i++)
            {
                state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
            }
        }
        if( state.xupdated )
        {
            *err = *err||ae_fp_greater(state.f,fprev);
            if( ae_fp_eq(fprev,ae_maxrealnumber) )
            {
                for(i=0; i<=n-1; i++)
                {
                    *err = *err||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                }
            }
            fprev = state.f;
            ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    minlbfgsresults(&state, &x, &rep, _state);
    for(i=0; i<=n-1; i++)
    {
        *err = *err||ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]);
    }
    
    /*
     * Test differentiation vs. analytic gradient
     * (first one issues NeedF requests, second one issues NeedFG requests)
     */
    n = 50;
    m = 5;
    diffstep = 1.0E-6;
    for(dkind=0; dkind<=1; dkind++)
    {
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 1;
        }
        if( dkind==0 )
        {
            minlbfgscreate(n, m, &x, &state, _state);
        }
        if( dkind==1 )
        {
            minlbfgscreatef(n, m, &x, diffstep, &state, _state);
        }
        minlbfgssetcond(&state, 0, 0, 0, n/2, _state);
        wasf = ae_false;
        wasfg = ae_false;
        while(minlbfgsiteration(&state, _state))
        {
            if( state.needf||state.needfg )
            {
                state.f = 0;
            }
            for(i=0; i<=n-1; i++)
            {
                if( state.needf||state.needfg )
                {
                    state.f = state.f+ae_sqr((1+i)*state.x.ptr.p_double[i], _state);
                }
                if( state.needfg )
                {
                    state.g.ptr.p_double[i] = 2*(1+i)*state.x.ptr.p_double[i];
                }
            }
            wasf = wasf||state.needf;
            wasfg = wasfg||state.needfg;
        }
        minlbfgsresults(&state, &x, &rep, _state);
        if( dkind==0 )
        {
            *err = (*err||wasf)||!wasfg;
        }
        if( dkind==1 )
        {
            *err = (*err||!wasf)||wasfg;
        }
    }
    
    /*
     * Test that numerical differentiation uses scaling.
     *
     * In order to test that we solve simple optimization
     * problem: min(x^2) with initial x equal to 0.0.
     *
     * We choose random DiffStep and S, then we check that
     * optimizer evaluates function at +-DiffStep*S only.
     */
    ae_vector_set_length(&x, 1, _state);
    ae_vector_set_length(&s, 1, _state);
    diffstep = ae_randomreal(_state)*1.0E-6;
    s.ptr.p_double[0] = ae_exp(ae_randomreal(_state)*4-2, _state);
    x.ptr.p_double[0] = 0;
    minlbfgscreatef(1, 1, &x, diffstep, &state, _state);
    minlbfgssetcond(&state, 1.0E-6, 0, 0, 0, _state);
    minlbfgssetscale(&state, &s, _state);
    v = 0;
    while(minlbfgsiteration(&state, _state))
    {
        state.f = ae_sqr(state.x.ptr.p_double[0], _state);
        v = ae_maxreal(v, ae_fabs(state.x.ptr.p_double[0], _state), _state);
    }
    minlbfgsresults(&state, &x, &rep, _state);
    r = v/(s.ptr.p_double[0]*diffstep);
    *err = *err||ae_fp_greater(ae_fabs(ae_log(r, _state), _state),ae_log(1+1000*ae_machineepsilon, _state));
    
    /*
     * test maximum step
     */
    n = 1;
    m = 1;
    ae_vector_set_length(&x, n, _state);
    x.ptr.p_double[0] = 100;
    stpmax = 0.05+0.05*ae_randomreal(_state);
    minlbfgscreate(n, m, &x, &state, _state);
    minlbfgssetcond(&state, 1.0E-9, 0, 0, 0, _state);
    minlbfgssetstpmax(&state, stpmax, _state);
    minlbfgssetxrep(&state, ae_true, _state);
    xprev = x.ptr.p_double[0];
    while(minlbfgsiteration(&state, _state))
    {
        if( state.needfg )
        {
            state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
            state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
            *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
        }
        if( state.xupdated )
        {
            *err = *err||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
            xprev = state.x.ptr.p_double[0];
        }
    }
    
    /*
     * Test correctness of the scaling:
     * * initial point is random point from [+1,+2]^N
     * * f(x) = SUM(A[i]*x[i]^4), C[i] is random from [0.01,100]
     * * we use random scaling matrix
     * * we test different variants of the preconditioning:
     *   0) unit preconditioner
     *   1) random diagonal from [0.01,100]
     *   2) scale preconditioner
     * * we set stringent stopping conditions (we try EpsG and EpsX)
     * * and we test that in the extremum stopping conditions are
     *   satisfied subject to the current scaling coefficients.
     */
    tmpeps = 1.0E-10;
    m = 1;
    for(n=1; n<=10; n++)
    {
        for(pkind=0; pkind<=2; pkind++)
        {
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&xlast, n, _state);
            ae_vector_set_length(&a, n, _state);
            ae_vector_set_length(&s, n, _state);
            ae_vector_set_length(&h, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_randomreal(_state)+1;
                a.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                s.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
                h.ptr.p_double[i] = ae_exp(ae_log(100, _state)*(2*ae_randomreal(_state)-1), _state);
            }
            minlbfgscreate(n, m, &x, &state, _state);
            minlbfgssetscale(&state, &s, _state);
            minlbfgssetxrep(&state, ae_true, _state);
            if( pkind==1 )
            {
                minlbfgssetprecdiag(&state, &h, _state);
            }
            if( pkind==2 )
            {
                minlbfgssetprecscale(&state, _state);
            }
            
            /*
             * Test gradient-based stopping condition
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_randomreal(_state)+1;
            }
            minlbfgssetcond(&state, tmpeps, 0, 0, 0, _state);
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 4, _state);
                        state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 3, _state);
                    }
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            v = 0;
            for(i=0; i<=n-1; i++)
            {
                v = v+ae_sqr(s.ptr.p_double[i]*4*a.ptr.p_double[i]*ae_pow(x.ptr.p_double[i], 3, _state), _state);
            }
            v = ae_sqrt(v, _state);
            *err = *err||ae_fp_greater(v,tmpeps);
            
            /*
             * Test step-based stopping condition
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_randomreal(_state)+1;
            }
            hasxlast = ae_false;
            minlbfgssetcond(&state, 0, 0, tmpeps, 0, _state);
            minlbfgsrestartfrom(&state, &x, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needfg )
                {
                    state.f = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 4, _state);
                        state.g.ptr.p_double[i] = 4*a.ptr.p_double[i]*ae_pow(state.x.ptr.p_double[i], 3, _state);
                    }
                }
                if( state.xupdated )
                {
                    if( hasxlast )
                    {
                        lastscaledstep = 0;
                        for(i=0; i<=n-1; i++)
                        {
                            lastscaledstep = lastscaledstep+ae_sqr(state.x.ptr.p_double[i]-xlast.ptr.p_double[i], _state)/ae_sqr(s.ptr.p_double[i], _state);
                        }
                        lastscaledstep = ae_sqrt(lastscaledstep, _state);
                    }
                    else
                    {
                        lastscaledstep = 0;
                    }
                    ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    hasxlast = ae_true;
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            *err = *err||ae_fp_greater(lastscaledstep,tmpeps);
        }
    }
    
    /*
     * Check correctness of the "trimming".
     *
     * Trimming is a technique which is used to help algorithm
     * cope with unbounded functions. In order to check this
     * technique we will try to solve following optimization
     * problem:
     *
     *     min f(x) subject to no constraints on X
     *            { 1/(1-x) + 1/(1+x) + c*x, if -0.999999<x<0.999999
     *     f(x) = {
     *            { M, if x<=-0.999999 or x>=0.999999
     *
     * where c is either 1.0 or 1.0E+6, M is either 1.0E8, 1.0E20 or +INF
     * (we try different combinations)
     */
    for(ckind=0; ckind<=1; ckind++)
    {
        for(mkind=0; mkind<=2; mkind++)
        {
            
            /*
             * Choose c and M
             */
            if( ckind==0 )
            {
                vc = 1.0;
            }
            if( ckind==1 )
            {
                vc = 1.0E+6;
            }
            if( mkind==0 )
            {
                vm = 1.0E+8;
            }
            if( mkind==1 )
            {
                vm = 1.0E+20;
            }
            if( mkind==2 )
            {
                vm = _state->v_posinf;
            }
            
            /*
             * Create optimizer, solve optimization problem
             */
            epsg = 1.0E-6*vc;
            ae_vector_set_length(&x, 1, _state);
            x.ptr.p_double[0] = 0.0;
            minlbfgscreate(1, 1, &x, &state, _state);
            minlbfgssetcond(&state, epsg, 0, 0, 0, _state);
            while(minlbfgsiteration(&state, _state))
            {
                if( state.needfg )
                {
                    if( ae_fp_less(-0.999999,state.x.ptr.p_double[0])&&ae_fp_less(state.x.ptr.p_double[0],0.999999) )
                    {
                        state.f = 1/(1-state.x.ptr.p_double[0])+1/(1+state.x.ptr.p_double[0])+vc*state.x.ptr.p_double[0];
                        state.g.ptr.p_double[0] = 1/ae_sqr(1-state.x.ptr.p_double[0], _state)-1/ae_sqr(1+state.x.ptr.p_double[0], _state)+vc;
                    }
                    else
                    {
                        state.f = vm;
                    }
                }
            }
            minlbfgsresults(&state, &x, &rep, _state);
            if( rep.terminationtype<=0 )
            {
                *err = ae_true;
                ae_frame_leave(_state);
                return;
            }
            *err = *err||ae_fp_greater(ae_fabs(1/ae_sqr(1-x.ptr.p_double[0], _state)-1/ae_sqr(1+x.ptr.p_double[0], _state)+vc, _state),epsg);
        }
    }
    ae_frame_leave(_state);
}



static void testmlptrainunit_createnetwork(multilayerperceptron* network,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_state *_state);
static void testmlptrainunit_unsetnetwork(multilayerperceptron* network,
     ae_state *_state);
static void testmlptrainunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlptrainunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlptrainunit_testgradient(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlptrainunit_testhessian(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);





ae_bool testmlptrain(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t maxhid;
    ae_int_t info;
    ae_int_t nf;
    ae_int_t nl;
    ae_int_t nhid1;
    ae_int_t nhid2;
    ae_int_t nkind;
    ae_int_t i;
    multilayerperceptron network;
    multilayerperceptron network2;
    mlpreport rep;
    mlpcvreport cvrep;
    ae_int_t ncount;
    ae_matrix xy;
    ae_matrix valxy;
    ae_bool inferrors;
    ae_bool procerrors;
    ae_bool graderrors;
    ae_bool hesserrors;
    ae_bool trnerrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    _multilayerperceptron_init(&network, _state, ae_true);
    _multilayerperceptron_init(&network2, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _mlpcvreport_init(&cvrep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&valxy, 0, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    inferrors = ae_false;
    procerrors = ae_false;
    graderrors = ae_false;
    hesserrors = ae_false;
    trnerrors = ae_false;
    passcount = 10;
    maxn = 4;
    maxhid = 4;
    
    /*
     * General multilayer network tests
     */
    for(nf=1; nf<=maxn; nf++)
    {
        for(nl=1; nl<=maxn; nl++)
        {
            for(nhid1=0; nhid1<=maxhid; nhid1++)
            {
                for(nhid2=0; nhid2<=0; nhid2++)
                {
                    for(nkind=0; nkind<=3; nkind++)
                    {
                        
                        /*
                         *  Skip meaningless parameters combinations
                         */
                        if( nkind==1&&nl<2 )
                        {
                            continue;
                        }
                        if( nhid1==0&&nhid2!=0 )
                        {
                            continue;
                        }
                        
                        /*
                         * Tests
                         */
                        testmlptrainunit_testinformational(nkind, nf, nhid1, nhid2, nl, passcount, &inferrors, _state);
                        testmlptrainunit_testprocessing(nkind, nf, nhid1, nhid2, nl, passcount, &procerrors, _state);
                        testmlptrainunit_testgradient(nkind, nf, nhid1, nhid2, nl, passcount, &graderrors, _state);
                        testmlptrainunit_testhessian(nkind, nf, nhid1, nhid2, nl, passcount, &hesserrors, _state);
                    }
                }
            }
        }
    }
    
    /*
     * Test network training on simple XOR problem
     */
    ae_matrix_set_length(&xy, 3+1, 2+1, _state);
    xy.ptr.pp_double[0][0] = -1;
    xy.ptr.pp_double[0][1] = -1;
    xy.ptr.pp_double[0][2] = -1;
    xy.ptr.pp_double[1][0] = 1;
    xy.ptr.pp_double[1][1] = -1;
    xy.ptr.pp_double[1][2] = 1;
    xy.ptr.pp_double[2][0] = -1;
    xy.ptr.pp_double[2][1] = 1;
    xy.ptr.pp_double[2][2] = 1;
    xy.ptr.pp_double[3][0] = 1;
    xy.ptr.pp_double[3][1] = 1;
    xy.ptr.pp_double[3][2] = -1;
    mlpcreate1(2, 2, 1, &network, _state);
    mlptrainlm(&network, &xy, 4, 0.001, 10, &info, &rep, _state);
    trnerrors = trnerrors||ae_fp_greater(mlprmserror(&network, &xy, 4, _state),0.1);
    
    /*
     * Test CV on random noisy problem
     */
    ncount = 100;
    ae_matrix_set_length(&xy, ncount-1+1, 1+1, _state);
    for(i=0; i<=ncount-1; i++)
    {
        xy.ptr.pp_double[i][0] = 2*ae_randomreal(_state)-1;
        xy.ptr.pp_double[i][1] = ae_randominteger(4, _state);
    }
    mlpcreatec0(1, 4, &network, _state);
    mlpkfoldcvlm(&network, &xy, ncount, 0.001, 5, 10, &info, &rep, &cvrep, _state);
    
    /*
     * Final report
     */
    waserrors = (((inferrors||procerrors)||graderrors)||hesserrors)||trnerrors;
    if( !silent )
    {
        printf("MLP TEST\n");
        printf("INFORMATIONAL FUNCTIONS:                 ");
        if( !inferrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BASIC PROCESSING:                        ");
        if( !procerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("GRADIENT CALCULATION:                    ");
        if( !graderrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("HESSIAN CALCULATION:                     ");
        if( !hesserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("TRAINING:                                ");
        if( !trnerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Network creation

This function creates network with desired structure. Network  is  created
using one of the three methods:
a) straighforward creation using MLPCreate???()
b) MLPCreate???() for proxy object, which is copied with PassThroughSerializer()
c) MLPCreate???() for proxy object, which is copied with MLPCopy()
One of these methods is chosen with probability 1/3.
*************************************************************************/
static void testmlptrainunit_createnetwork(multilayerperceptron* network,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t mkind;
    multilayerperceptron tmp;

    ae_frame_make(_state, &_frame_block);
    _multilayerperceptron_init(&tmp, _state, ae_true);

    ae_assert(((nin>0&&nhid1>=0)&&nhid2>=0)&&nout>0, "CreateNetwork error", _state);
    ae_assert(nhid1!=0||nhid2==0, "CreateNetwork error", _state);
    ae_assert(nkind!=1||nout>=2, "CreateNetwork error", _state);
    mkind = ae_randominteger(3, _state);
    if( nhid1==0 )
    {
        
        /*
         * No hidden layers
         */
        if( nkind==0 )
        {
            if( mkind==0 )
            {
                mlpcreate0(nin, nout, network, _state);
            }
            if( mkind==1 )
            {
                mlpcreate0(nin, nout, &tmp, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpalloc(&_local_serializer, &tmp, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpserialize(&_local_serializer, &tmp, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpunserialize(&_local_serializer, network, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
            }
            if( mkind==2 )
            {
                mlpcreate0(nin, nout, &tmp, _state);
                mlpcopy(&tmp, network, _state);
            }
        }
        else
        {
            if( nkind==1 )
            {
                if( mkind==0 )
                {
                    mlpcreatec0(nin, nout, network, _state);
                }
                if( mkind==1 )
                {
                    mlpcreatec0(nin, nout, &tmp, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        mlpalloc(&_local_serializer, &tmp, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpserialize(&_local_serializer, &tmp, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpunserialize(&_local_serializer, network, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                }
                if( mkind==2 )
                {
                    mlpcreatec0(nin, nout, &tmp, _state);
                    mlpcopy(&tmp, network, _state);
                }
            }
            else
            {
                if( nkind==2 )
                {
                    if( mkind==0 )
                    {
                        mlpcreateb0(nin, nout, a1, a2, network, _state);
                    }
                    if( mkind==1 )
                    {
                        mlpcreateb0(nin, nout, a1, a2, &tmp, _state);
                        {
                            /*
                             * This code passes data structure through serializers
                             * (serializes it to string and loads back)
                             */
                            ae_serializer _local_serializer;
                            ae_int_t _local_ssize;
                            ae_frame _local_frame_block;
                            ae_dyn_block _local_dynamic_block;
                            
                            ae_frame_make(_state, &_local_frame_block);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_alloc_start(&_local_serializer);
                            mlpalloc(&_local_serializer, &tmp, _state);
                            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                            ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpserialize(&_local_serializer, &tmp, _state);
                            ae_serializer_stop(&_local_serializer);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpunserialize(&_local_serializer, network, _state);
                            ae_serializer_stop(&_local_serializer);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_frame_leave(_state);
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpcreateb0(nin, nout, a1, a2, &tmp, _state);
                        mlpcopy(&tmp, network, _state);
                    }
                }
                else
                {
                    if( nkind==3 )
                    {
                        if( mkind==0 )
                        {
                            mlpcreater0(nin, nout, a1, a2, network, _state);
                        }
                        if( mkind==1 )
                        {
                            mlpcreater0(nin, nout, a1, a2, &tmp, _state);
                            {
                                /*
                                 * This code passes data structure through serializers
                                 * (serializes it to string and loads back)
                                 */
                                ae_serializer _local_serializer;
                                ae_int_t _local_ssize;
                                ae_frame _local_frame_block;
                                ae_dyn_block _local_dynamic_block;
                                
                                ae_frame_make(_state, &_local_frame_block);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_alloc_start(&_local_serializer);
                                mlpalloc(&_local_serializer, &tmp, _state);
                                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                                ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpserialize(&_local_serializer, &tmp, _state);
                                ae_serializer_stop(&_local_serializer);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpunserialize(&_local_serializer, network, _state);
                                ae_serializer_stop(&_local_serializer);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_frame_leave(_state);
                            }
                        }
                        if( mkind==2 )
                        {
                            mlpcreater0(nin, nout, a1, a2, &tmp, _state);
                            mlpcopy(&tmp, network, _state);
                        }
                    }
                }
            }
        }
        mlprandomizefull(network, _state);
        ae_frame_leave(_state);
        return;
    }
    if( nhid2==0 )
    {
        
        /*
         * One hidden layer
         */
        if( nkind==0 )
        {
            if( mkind==0 )
            {
                mlpcreate1(nin, nhid1, nout, network, _state);
            }
            if( mkind==1 )
            {
                mlpcreate1(nin, nhid1, nout, &tmp, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpalloc(&_local_serializer, &tmp, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpserialize(&_local_serializer, &tmp, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpunserialize(&_local_serializer, network, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
            }
            if( mkind==2 )
            {
                mlpcreate1(nin, nhid1, nout, &tmp, _state);
                mlpcopy(&tmp, network, _state);
            }
        }
        else
        {
            if( nkind==1 )
            {
                if( mkind==0 )
                {
                    mlpcreatec1(nin, nhid1, nout, network, _state);
                }
                if( mkind==1 )
                {
                    mlpcreatec1(nin, nhid1, nout, &tmp, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        mlpalloc(&_local_serializer, &tmp, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpserialize(&_local_serializer, &tmp, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpunserialize(&_local_serializer, network, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                }
                if( mkind==2 )
                {
                    mlpcreatec1(nin, nhid1, nout, &tmp, _state);
                    mlpcopy(&tmp, network, _state);
                }
            }
            else
            {
                if( nkind==2 )
                {
                    if( mkind==0 )
                    {
                        mlpcreateb1(nin, nhid1, nout, a1, a2, network, _state);
                    }
                    if( mkind==1 )
                    {
                        mlpcreateb1(nin, nhid1, nout, a1, a2, &tmp, _state);
                        {
                            /*
                             * This code passes data structure through serializers
                             * (serializes it to string and loads back)
                             */
                            ae_serializer _local_serializer;
                            ae_int_t _local_ssize;
                            ae_frame _local_frame_block;
                            ae_dyn_block _local_dynamic_block;
                            
                            ae_frame_make(_state, &_local_frame_block);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_alloc_start(&_local_serializer);
                            mlpalloc(&_local_serializer, &tmp, _state);
                            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                            ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpserialize(&_local_serializer, &tmp, _state);
                            ae_serializer_stop(&_local_serializer);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpunserialize(&_local_serializer, network, _state);
                            ae_serializer_stop(&_local_serializer);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_frame_leave(_state);
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpcreateb1(nin, nhid1, nout, a1, a2, &tmp, _state);
                        mlpcopy(&tmp, network, _state);
                    }
                }
                else
                {
                    if( nkind==3 )
                    {
                        if( mkind==0 )
                        {
                            mlpcreater1(nin, nhid1, nout, a1, a2, network, _state);
                        }
                        if( mkind==1 )
                        {
                            mlpcreater1(nin, nhid1, nout, a1, a2, &tmp, _state);
                            {
                                /*
                                 * This code passes data structure through serializers
                                 * (serializes it to string and loads back)
                                 */
                                ae_serializer _local_serializer;
                                ae_int_t _local_ssize;
                                ae_frame _local_frame_block;
                                ae_dyn_block _local_dynamic_block;
                                
                                ae_frame_make(_state, &_local_frame_block);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_alloc_start(&_local_serializer);
                                mlpalloc(&_local_serializer, &tmp, _state);
                                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                                ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpserialize(&_local_serializer, &tmp, _state);
                                ae_serializer_stop(&_local_serializer);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_serializer_init(&_local_serializer);
                                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                                mlpunserialize(&_local_serializer, network, _state);
                                ae_serializer_stop(&_local_serializer);
                                ae_serializer_clear(&_local_serializer);
                                
                                ae_frame_leave(_state);
                            }
                        }
                        if( mkind==2 )
                        {
                            mlpcreater1(nin, nhid1, nout, a1, a2, &tmp, _state);
                            mlpcopy(&tmp, network, _state);
                        }
                    }
                }
            }
        }
        mlprandomizefull(network, _state);
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * Two hidden layers
     */
    if( nkind==0 )
    {
        if( mkind==0 )
        {
            mlpcreate2(nin, nhid1, nhid2, nout, network, _state);
        }
        if( mkind==1 )
        {
            mlpcreate2(nin, nhid1, nhid2, nout, &tmp, _state);
            {
                /*
                 * This code passes data structure through serializers
                 * (serializes it to string and loads back)
                 */
                ae_serializer _local_serializer;
                ae_int_t _local_ssize;
                ae_frame _local_frame_block;
                ae_dyn_block _local_dynamic_block;
                
                ae_frame_make(_state, &_local_frame_block);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_alloc_start(&_local_serializer);
                mlpalloc(&_local_serializer, &tmp, _state);
                _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                mlpserialize(&_local_serializer, &tmp, _state);
                ae_serializer_stop(&_local_serializer);
                ae_serializer_clear(&_local_serializer);
                
                ae_serializer_init(&_local_serializer);
                ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                mlpunserialize(&_local_serializer, network, _state);
                ae_serializer_stop(&_local_serializer);
                ae_serializer_clear(&_local_serializer);
                
                ae_frame_leave(_state);
            }
        }
        if( mkind==2 )
        {
            mlpcreate2(nin, nhid1, nhid2, nout, &tmp, _state);
            mlpcopy(&tmp, network, _state);
        }
    }
    else
    {
        if( nkind==1 )
        {
            if( mkind==0 )
            {
                mlpcreatec2(nin, nhid1, nhid2, nout, network, _state);
            }
            if( mkind==1 )
            {
                mlpcreatec2(nin, nhid1, nhid2, nout, &tmp, _state);
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    mlpalloc(&_local_serializer, &tmp, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpserialize(&_local_serializer, &tmp, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    mlpunserialize(&_local_serializer, network, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
            }
            if( mkind==2 )
            {
                mlpcreatec2(nin, nhid1, nhid2, nout, &tmp, _state);
                mlpcopy(&tmp, network, _state);
            }
        }
        else
        {
            if( nkind==2 )
            {
                if( mkind==0 )
                {
                    mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, network, _state);
                }
                if( mkind==1 )
                {
                    mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                    {
                        /*
                         * This code passes data structure through serializers
                         * (serializes it to string and loads back)
                         */
                        ae_serializer _local_serializer;
                        ae_int_t _local_ssize;
                        ae_frame _local_frame_block;
                        ae_dyn_block _local_dynamic_block;
                        
                        ae_frame_make(_state, &_local_frame_block);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_alloc_start(&_local_serializer);
                        mlpalloc(&_local_serializer, &tmp, _state);
                        _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                        ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                        ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpserialize(&_local_serializer, &tmp, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_serializer_init(&_local_serializer);
                        ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                        mlpunserialize(&_local_serializer, network, _state);
                        ae_serializer_stop(&_local_serializer);
                        ae_serializer_clear(&_local_serializer);
                        
                        ae_frame_leave(_state);
                    }
                }
                if( mkind==2 )
                {
                    mlpcreateb2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                    mlpcopy(&tmp, network, _state);
                }
            }
            else
            {
                if( nkind==3 )
                {
                    if( mkind==0 )
                    {
                        mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, network, _state);
                    }
                    if( mkind==1 )
                    {
                        mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                        {
                            /*
                             * This code passes data structure through serializers
                             * (serializes it to string and loads back)
                             */
                            ae_serializer _local_serializer;
                            ae_int_t _local_ssize;
                            ae_frame _local_frame_block;
                            ae_dyn_block _local_dynamic_block;
                            
                            ae_frame_make(_state, &_local_frame_block);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_alloc_start(&_local_serializer);
                            mlpalloc(&_local_serializer, &tmp, _state);
                            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                            ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpserialize(&_local_serializer, &tmp, _state);
                            ae_serializer_stop(&_local_serializer);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_serializer_init(&_local_serializer);
                            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                            mlpunserialize(&_local_serializer, network, _state);
                            ae_serializer_stop(&_local_serializer);
                            ae_serializer_clear(&_local_serializer);
                            
                            ae_frame_leave(_state);
                        }
                    }
                    if( mkind==2 )
                    {
                        mlpcreater2(nin, nhid1, nhid2, nout, a1, a2, &tmp, _state);
                        mlpcopy(&tmp, network, _state);
                    }
                }
            }
        }
    }
    mlprandomizefull(network, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets network (initialize it to smallest network possible
*************************************************************************/
static void testmlptrainunit_unsetnetwork(multilayerperceptron* network,
     ae_state *_state)
{


    mlpcreate0(1, 1, network, _state);
}


/*************************************************************************
Informational functions test
*************************************************************************/
static void testmlptrainunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double threshold;
    ae_int_t nlayers;
    ae_int_t nmax;
    ae_bool issoftmax;
    ae_matrix neurons;
    ae_vector x;
    ae_vector y;
    double mean;
    double sigma;
    ae_int_t fkind;
    double c;
    double f;
    double df;
    double d2f;
    double s;

    ae_frame_make(_state, &_frame_block);
    _multilayerperceptron_init(&network, _state, ae_true);
    ae_matrix_init(&neurons, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);

    threshold = 100000*ae_machineepsilon;
    testmlptrainunit_createnetwork(&network, nkind, 0.0, 0.0, nin, nhid1, nhid2, nout, _state);
    
    /*
     * test MLPProperties()
     */
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    *err = ((*err||n1!=nin)||n2!=nout)||wcount<=0;
    
    /*
     * Test network geometry functions
     *
     * In order to do this we calculate neural network output using
     * informational functions only, and compare results with ones
     * obtained with MLPProcess():
     * 1. we allocate 2-dimensional array of neurons and fill it by zeros
     * 2. we full first layer of neurons by input values
     * 3. we move through array, calculating values of subsequent layers
     * 4. if we have classification network, we SOFTMAX-normalize output layer
     * 5. we apply scaling to the outputs
     * 6. we compare results with ones obtained by MLPProcess()
     *
     * NOTE: it is important to do (4) before (5), because on SOFTMAX network
     *       MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order
     *       to test it implicitly, we apply it to the classifier results
     *       (already normalized). If one of the coefficients deviates from
     *       expected values, we will get error during (6).
     */
    nlayers = 2;
    nmax = ae_maxint(nin, nout, _state);
    issoftmax = nkind==1;
    if( nhid1!=0 )
    {
        nlayers = 3;
        nmax = ae_maxint(nmax, nhid1, _state);
    }
    if( nhid2!=0 )
    {
        nlayers = 4;
        nmax = ae_maxint(nmax, nhid2, _state);
    }
    ae_matrix_set_length(&neurons, nlayers, nmax, _state);
    for(i=0; i<=nlayers-1; i++)
    {
        for(j=0; j<=nmax-1; j++)
        {
            neurons.ptr.pp_double[i][j] = 0;
        }
    }
    ae_vector_set_length(&x, nin, _state);
    for(i=0; i<=nin-1; i++)
    {
        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
    }
    ae_vector_set_length(&y, nout, _state);
    for(i=0; i<=nout-1; i++)
    {
        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
    }
    for(j=0; j<=nin-1; j++)
    {
        mlpgetinputscaling(&network, j, &mean, &sigma, _state);
        neurons.ptr.pp_double[0][j] = (x.ptr.p_double[j]-mean)/sigma;
    }
    for(i=1; i<=nlayers-1; i++)
    {
        for(j=0; j<=mlpgetlayersize(&network, i, _state)-1; j++)
        {
            for(k=0; k<=mlpgetlayersize(&network, i-1, _state)-1; k++)
            {
                neurons.ptr.pp_double[i][j] = neurons.ptr.pp_double[i][j]+mlpgetweight(&network, i-1, k, i, j, _state)*neurons.ptr.pp_double[i-1][k];
            }
            mlpgetneuroninfo(&network, i, j, &fkind, &c, _state);
            mlpactivationfunction(neurons.ptr.pp_double[i][j]-c, fkind, &f, &df, &d2f, _state);
            neurons.ptr.pp_double[i][j] = f;
        }
    }
    if( nkind==1 )
    {
        s = 0;
        for(j=0; j<=nout-1; j++)
        {
            s = s+ae_exp(neurons.ptr.pp_double[nlayers-1][j], _state);
        }
        for(j=0; j<=nout-1; j++)
        {
            neurons.ptr.pp_double[nlayers-1][j] = ae_exp(neurons.ptr.pp_double[nlayers-1][j], _state)/s;
        }
    }
    for(j=0; j<=nout-1; j++)
    {
        mlpgetoutputscaling(&network, j, &mean, &sigma, _state);
        neurons.ptr.pp_double[nlayers-1][j] = neurons.ptr.pp_double[nlayers-1][j]*sigma+mean;
    }
    mlpprocess(&network, &x, &y, _state);
    for(j=0; j<=nout-1; j++)
    {
        *err = *err||ae_fp_greater(ae_fabs(neurons.ptr.pp_double[nlayers-1][j]-y.ptr.p_double[j], _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Processing functions test
*************************************************************************/
static void testmlptrainunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    multilayerperceptron network2;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    ae_bool zeronet;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t i;
    ae_bool allsame;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    double v;

    ae_frame_make(_state, &_frame_block);
    _multilayerperceptron_init(&network, _state, ae_true);
    _multilayerperceptron_init(&network2, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);

    ae_assert(passcount>=2, "PassCount<2!", _state);
    
    /*
     * Prepare network
     */
    a1 = 0;
    a2 = 0;
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlptrainunit_createnetwork(&network, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    
    /*
     * Initialize arrays
     */
    ae_vector_set_length(&x1, nin-1+1, _state);
    ae_vector_set_length(&x2, nin-1+1, _state);
    ae_vector_set_length(&y1, nout-1+1, _state);
    ae_vector_set_length(&y2, nout-1+1, _state);
    
    /*
     * Main cycle
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Last run is made on zero network
         */
        mlprandomizefull(&network, _state);
        zeronet = ae_false;
        if( pass==passcount )
        {
            ae_v_muld(&network.weights.ptr.p_double[0], 1, ae_v_len(0,wcount-1), 0);
            zeronet = ae_true;
        }
        
        /*
         * Same inputs leads to same outputs
         */
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original network leads to same outputs
         * on copy created using MLPCopy
         */
        testmlptrainunit_unsetnetwork(&network2, _state);
        mlpcopy(&network, &network2, _state);
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original network leads to same outputs
         * on copy created using MLPSerialize
         */
        testmlptrainunit_unsetnetwork(&network2, _state);
        {
            /*
             * This code passes data structure through serializers
             * (serializes it to string and loads back)
             */
            ae_serializer _local_serializer;
            ae_int_t _local_ssize;
            ae_frame _local_frame_block;
            ae_dyn_block _local_dynamic_block;
            
            ae_frame_make(_state, &_local_frame_block);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_alloc_start(&_local_serializer);
            mlpalloc(&_local_serializer, &network, _state);
            _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
            ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
            ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            mlpserialize(&_local_serializer, &network, _state);
            ae_serializer_stop(&_local_serializer);
            ae_serializer_clear(&_local_serializer);
            
            ae_serializer_init(&_local_serializer);
            ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
            mlpunserialize(&_local_serializer, &network2, _state);
            ae_serializer_stop(&_local_serializer);
            ae_serializer_clear(&_local_serializer);
            
            ae_frame_leave(_state);
        }
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpprocess(&network, &x1, &y1, _state);
        mlpprocess(&network2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Different inputs leads to different outputs (non-zero network)
         */
        if( !zeronet )
        {
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpprocess(&network, &x1, &y1, _state);
            mlpprocess(&network, &x2, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
        }
        
        /*
         * Randomization changes outputs (when inputs are unchanged, non-zero network)
         */
        if( !zeronet )
        {
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpcopy(&network, &network2, _state);
            mlprandomize(&network2, _state);
            mlpprocess(&network, &x1, &y1, _state);
            mlpprocess(&network2, &x1, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
        }
        
        /*
         * Full randomization changes outputs (when inputs are unchanged, non-zero network)
         */
        if( !zeronet )
        {
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            for(i=0; i<=nout-1; i++)
            {
                y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y2.ptr.p_double[i] = y1.ptr.p_double[i];
            }
            mlpcopy(&network, &network2, _state);
            mlprandomizefull(&network2, _state);
            mlpprocess(&network, &x1, &y1, _state);
            mlpprocess(&network2, &x1, &y2, _state);
            allsame = ae_true;
            for(i=0; i<=nout-1; i++)
            {
                allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
            }
            *err = *err||allsame;
        }
        
        /*
         * Normalization properties
         */
        if( nkind==1 )
        {
            
            /*
             * Classifier network outputs are normalized
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpprocess(&network, &x1, &y1, _state);
            v = 0;
            for(i=0; i<=nout-1; i++)
            {
                v = v+y1.ptr.p_double[i];
                *err = *err||ae_fp_less(y1.ptr.p_double[i],0);
            }
            *err = *err||ae_fp_greater(ae_fabs(v-1, _state),1000*ae_machineepsilon);
        }
        if( nkind==2 )
        {
            
            /*
             * B-type network outputs are bounded from above/below
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpprocess(&network, &x1, &y1, _state);
            for(i=0; i<=nout-1; i++)
            {
                if( ae_fp_greater_eq(a2,0) )
                {
                    *err = *err||ae_fp_less(y1.ptr.p_double[i],a1);
                }
                else
                {
                    *err = *err||ae_fp_greater(y1.ptr.p_double[i],a1);
                }
            }
        }
        if( nkind==3 )
        {
            
            /*
             * R-type network outputs are within [A1,A2] (or [A2,A1])
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpprocess(&network, &x1, &y1, _state);
            for(i=0; i<=nout-1; i++)
            {
                *err = (*err||ae_fp_less(y1.ptr.p_double[i],ae_minreal(a1, a2, _state)))||ae_fp_greater(y1.ptr.p_double[i],ae_maxreal(a1, a2, _state));
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Gradient functions test
*************************************************************************/
static void testmlptrainunit_testgradient(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    double h;
    double etol;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ssize;
    ae_matrix xy;
    ae_vector grad1;
    ae_vector grad2;
    ae_vector x;
    ae_vector y;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    double v;
    double e;
    double e1;
    double e2;
    double v1;
    double v2;
    double v3;
    double v4;
    double wprev;

    ae_frame_make(_state, &_frame_block);
    _multilayerperceptron_init(&network, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);

    ae_assert(passcount>=2, "PassCount<2!", _state);
    a1 = 0;
    a2 = 0;
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlptrainunit_createnetwork(&network, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    h = 0.0001;
    etol = 0.01;
    
    /*
     * Initialize
     */
    ae_vector_set_length(&x, nin-1+1, _state);
    ae_vector_set_length(&x1, nin-1+1, _state);
    ae_vector_set_length(&x2, nin-1+1, _state);
    ae_vector_set_length(&y, nout-1+1, _state);
    ae_vector_set_length(&y1, nout-1+1, _state);
    ae_vector_set_length(&y2, nout-1+1, _state);
    ae_vector_set_length(&grad1, wcount-1+1, _state);
    ae_vector_set_length(&grad2, wcount-1+1, _state);
    
    /*
     * Process
     */
    for(pass=1; pass<=passcount; pass++)
    {
        mlprandomizefull(&network, _state);
        
        /*
         * Test error/gradient calculation (least squares)
         */
        ae_matrix_set_length(&xy, 0+1, nin+nout-1+1, _state);
        for(i=0; i<=nin-1; i++)
        {
            x.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
        }
        ae_v_move(&xy.ptr.pp_double[0][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nin-1));
        if( mlpissoftmax(&network, _state) )
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = 0;
            }
            xy.ptr.pp_double[0][nin] = ae_randominteger(nout, _state);
            y.ptr.p_double[ae_round(xy.ptr.pp_double[0][nin], _state)] = 1;
        }
        else
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
            }
            ae_v_move(&xy.ptr.pp_double[0][nin], 1, &y.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
        }
        mlpgrad(&network, &x, &y, &e, &grad2, _state);
        mlpprocess(&network, &x, &y2, _state);
        ae_v_sub(&y2.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
        v = ae_v_dotproduct(&y2.ptr.p_double[0], 1, &y2.ptr.p_double[0], 1, ae_v_len(0,nout-1));
        v = v/2;
        *err = *err||ae_fp_greater(ae_fabs((v-e)/v, _state),etol);
        *err = *err||ae_fp_greater(ae_fabs((mlperror(&network, &xy, 1, _state)-v)/v, _state),etol);
        for(i=0; i<=wcount-1; i++)
        {
            wprev = network.weights.ptr.p_double[i];
            network.weights.ptr.p_double[i] = wprev-2*h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v1 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v1 = v1/2;
            network.weights.ptr.p_double[i] = wprev-h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v2 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v2 = v2/2;
            network.weights.ptr.p_double[i] = wprev+h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v3 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v3 = v3/2;
            network.weights.ptr.p_double[i] = wprev+2*h;
            mlpprocess(&network, &x, &y1, _state);
            ae_v_sub(&y1.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v4 = ae_v_dotproduct(&y1.ptr.p_double[0], 1, &y1.ptr.p_double[0], 1, ae_v_len(0,nout-1));
            v4 = v4/2;
            network.weights.ptr.p_double[i] = wprev;
            grad1.ptr.p_double[i] = (v1-8*v2+8*v3-v4)/(12*h);
            if( ae_fp_greater(ae_fabs(grad1.ptr.p_double[i], _state),1.0E-3) )
            {
                *err = *err||ae_fp_greater(ae_fabs((grad2.ptr.p_double[i]-grad1.ptr.p_double[i])/grad1.ptr.p_double[i], _state),etol);
            }
            else
            {
                *err = *err||ae_fp_greater(ae_fabs(grad2.ptr.p_double[i]-grad1.ptr.p_double[i], _state),etol);
            }
        }
        
        /*
         * Test error/gradient calculation (natural).
         * Testing on non-random structure networks
         * (because NKind is representative only in that case).
         */
        ae_matrix_set_length(&xy, 0+1, nin+nout-1+1, _state);
        for(i=0; i<=nin-1; i++)
        {
            x.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
        }
        ae_v_move(&xy.ptr.pp_double[0][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nin-1));
        if( mlpissoftmax(&network, _state) )
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = 0;
            }
            xy.ptr.pp_double[0][nin] = ae_randominteger(nout, _state);
            y.ptr.p_double[ae_round(xy.ptr.pp_double[0][nin], _state)] = 1;
        }
        else
        {
            for(i=0; i<=nout-1; i++)
            {
                y.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
            }
            ae_v_move(&xy.ptr.pp_double[0][nin], 1, &y.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
        }
        mlpgradn(&network, &x, &y, &e, &grad2, _state);
        mlpprocess(&network, &x, &y2, _state);
        v = 0;
        if( nkind!=1 )
        {
            for(i=0; i<=nout-1; i++)
            {
                v = v+0.5*ae_sqr(y2.ptr.p_double[i]-y.ptr.p_double[i], _state);
            }
        }
        else
        {
            for(i=0; i<=nout-1; i++)
            {
                if( ae_fp_neq(y.ptr.p_double[i],0) )
                {
                    if( ae_fp_eq(y2.ptr.p_double[i],0) )
                    {
                        v = v+y.ptr.p_double[i]*ae_log(ae_maxrealnumber, _state);
                    }
                    else
                    {
                        v = v+y.ptr.p_double[i]*ae_log(y.ptr.p_double[i]/y2.ptr.p_double[i], _state);
                    }
                }
            }
        }
        *err = *err||ae_fp_greater(ae_fabs((v-e)/v, _state),etol);
        *err = *err||ae_fp_greater(ae_fabs((mlperrorn(&network, &xy, 1, _state)-v)/v, _state),etol);
        for(i=0; i<=wcount-1; i++)
        {
            wprev = network.weights.ptr.p_double[i];
            network.weights.ptr.p_double[i] = wprev+h;
            mlpprocess(&network, &x, &y2, _state);
            network.weights.ptr.p_double[i] = wprev-h;
            mlpprocess(&network, &x, &y1, _state);
            network.weights.ptr.p_double[i] = wprev;
            v = 0;
            if( nkind!=1 )
            {
                for(j=0; j<=nout-1; j++)
                {
                    v = v+0.5*(ae_sqr(y2.ptr.p_double[j]-y.ptr.p_double[j], _state)-ae_sqr(y1.ptr.p_double[j]-y.ptr.p_double[j], _state))/(2*h);
                }
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    if( ae_fp_neq(y.ptr.p_double[j],0) )
                    {
                        if( ae_fp_eq(y2.ptr.p_double[j],0) )
                        {
                            v = v+y.ptr.p_double[j]*ae_log(ae_maxrealnumber, _state);
                        }
                        else
                        {
                            v = v+y.ptr.p_double[j]*ae_log(y.ptr.p_double[j]/y2.ptr.p_double[j], _state);
                        }
                        if( ae_fp_eq(y1.ptr.p_double[j],0) )
                        {
                            v = v-y.ptr.p_double[j]*ae_log(ae_maxrealnumber, _state);
                        }
                        else
                        {
                            v = v-y.ptr.p_double[j]*ae_log(y.ptr.p_double[j]/y1.ptr.p_double[j], _state);
                        }
                    }
                }
                v = v/(2*h);
            }
            grad1.ptr.p_double[i] = v;
            if( ae_fp_greater(ae_fabs(grad1.ptr.p_double[i], _state),1.0E-3) )
            {
                *err = *err||ae_fp_greater(ae_fabs((grad2.ptr.p_double[i]-grad1.ptr.p_double[i])/grad1.ptr.p_double[i], _state),etol);
            }
            else
            {
                *err = *err||ae_fp_greater(ae_fabs(grad2.ptr.p_double[i]-grad1.ptr.p_double[i], _state),etol);
            }
        }
        
        /*
         * Test gradient calculation: batch (least squares)
         */
        ssize = 1+ae_randominteger(10, _state);
        ae_matrix_set_length(&xy, ssize-1+1, nin+nout-1+1, _state);
        for(i=0; i<=wcount-1; i++)
        {
            grad1.ptr.p_double[i] = 0;
        }
        e1 = 0;
        for(i=0; i<=ssize-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
            }
            ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
            if( mlpissoftmax(&network, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 0;
                }
                xy.ptr.pp_double[i][nin] = ae_randominteger(nout, _state);
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = 1;
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                }
                ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
            }
            mlpgrad(&network, &x1, &y1, &v, &grad2, _state);
            e1 = e1+v;
            ae_v_add(&grad1.ptr.p_double[0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
        }
        mlpgradbatch(&network, &xy, ssize, &e2, &grad2, _state);
        *err = *err||ae_fp_greater(ae_fabs(e1-e2, _state)/e1,0.01);
        for(i=0; i<=wcount-1; i++)
        {
            if( ae_fp_neq(grad1.ptr.p_double[i],0) )
            {
                *err = *err||ae_fp_greater(ae_fabs((grad2.ptr.p_double[i]-grad1.ptr.p_double[i])/grad1.ptr.p_double[i], _state),etol);
            }
            else
            {
                *err = *err||ae_fp_neq(grad2.ptr.p_double[i],grad1.ptr.p_double[i]);
            }
        }
        
        /*
         * Test gradient calculation: batch (natural error func)
         */
        ssize = 1+ae_randominteger(10, _state);
        ae_matrix_set_length(&xy, ssize-1+1, nin+nout-1+1, _state);
        for(i=0; i<=wcount-1; i++)
        {
            grad1.ptr.p_double[i] = 0;
        }
        e1 = 0;
        for(i=0; i<=ssize-1; i++)
        {
            for(j=0; j<=nin-1; j++)
            {
                x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
            }
            ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
            if( mlpissoftmax(&network, _state) )
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 0;
                }
                xy.ptr.pp_double[i][nin] = ae_randominteger(nout, _state);
                y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = 1;
            }
            else
            {
                for(j=0; j<=nout-1; j++)
                {
                    y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                }
                ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
            }
            mlpgradn(&network, &x1, &y1, &v, &grad2, _state);
            e1 = e1+v;
            ae_v_add(&grad1.ptr.p_double[0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
        }
        mlpgradnbatch(&network, &xy, ssize, &e2, &grad2, _state);
        *err = *err||ae_fp_greater(ae_fabs(e1-e2, _state)/e1,etol);
        for(i=0; i<=wcount-1; i++)
        {
            if( ae_fp_neq(grad1.ptr.p_double[i],0) )
            {
                *err = *err||ae_fp_greater(ae_fabs((grad2.ptr.p_double[i]-grad1.ptr.p_double[i])/grad1.ptr.p_double[i], _state),etol);
            }
            else
            {
                *err = *err||ae_fp_neq(grad2.ptr.p_double[i],grad1.ptr.p_double[i]);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Hessian functions test
*************************************************************************/
static void testmlptrainunit_testhessian(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    multilayerperceptron network;
    ae_int_t hkind;
    ae_int_t n1;
    ae_int_t n2;
    ae_int_t wcount;
    double h;
    double etol;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t ssize;
    double a1;
    double a2;
    ae_matrix xy;
    ae_matrix h1;
    ae_matrix h2;
    ae_vector grad1;
    ae_vector grad2;
    ae_vector grad3;
    ae_vector x;
    ae_vector y;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    double v;
    double e1;
    double e2;
    double wprev;

    ae_frame_make(_state, &_frame_block);
    _multilayerperceptron_init(&network, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&h2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&grad3, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);

    ae_assert(passcount>=2, "PassCount<2!", _state);
    a1 = 0;
    a2 = 0;
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    testmlptrainunit_createnetwork(&network, nkind, a1, a2, nin, nhid1, nhid2, nout, _state);
    mlpproperties(&network, &n1, &n2, &wcount, _state);
    h = 0.0001;
    etol = 0.05;
    
    /*
     * Initialize
     */
    ae_vector_set_length(&x, nin-1+1, _state);
    ae_vector_set_length(&x1, nin-1+1, _state);
    ae_vector_set_length(&x2, nin-1+1, _state);
    ae_vector_set_length(&y, nout-1+1, _state);
    ae_vector_set_length(&y1, nout-1+1, _state);
    ae_vector_set_length(&y2, nout-1+1, _state);
    ae_vector_set_length(&grad1, wcount-1+1, _state);
    ae_vector_set_length(&grad2, wcount-1+1, _state);
    ae_vector_set_length(&grad3, wcount-1+1, _state);
    ae_matrix_set_length(&h1, wcount-1+1, wcount-1+1, _state);
    ae_matrix_set_length(&h2, wcount-1+1, wcount-1+1, _state);
    
    /*
     * Process
     */
    for(pass=1; pass<=passcount; pass++)
    {
        mlprandomizefull(&network, _state);
        
        /*
         * Test hessian calculation .
         * E1 contains total error (calculated using MLPGrad/MLPGradN)
         * Grad1 contains total gradient (calculated using MLPGrad/MLPGradN)
         * H1 contains Hessian calculated using differences of gradients
         *
         * E2, Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch
         */
        for(hkind=0; hkind<=1; hkind++)
        {
            ssize = 1+ae_randominteger(10, _state);
            ae_matrix_set_length(&xy, ssize-1+1, nin+nout-1+1, _state);
            for(i=0; i<=wcount-1; i++)
            {
                grad1.ptr.p_double[i] = 0;
            }
            for(i=0; i<=wcount-1; i++)
            {
                for(j=0; j<=wcount-1; j++)
                {
                    h1.ptr.pp_double[i][j] = 0;
                }
            }
            e1 = 0;
            for(i=0; i<=ssize-1; i++)
            {
                
                /*
                 * X, Y
                 */
                for(j=0; j<=nin-1; j++)
                {
                    x1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                }
                ae_v_move(&xy.ptr.pp_double[i][0], 1, &x1.ptr.p_double[0], 1, ae_v_len(0,nin-1));
                if( mlpissoftmax(&network, _state) )
                {
                    for(j=0; j<=nout-1; j++)
                    {
                        y1.ptr.p_double[j] = 0;
                    }
                    xy.ptr.pp_double[i][nin] = ae_randominteger(nout, _state);
                    y1.ptr.p_double[ae_round(xy.ptr.pp_double[i][nin], _state)] = 1;
                }
                else
                {
                    for(j=0; j<=nout-1; j++)
                    {
                        y1.ptr.p_double[j] = 4*ae_randomreal(_state)-2;
                    }
                    ae_v_move(&xy.ptr.pp_double[i][nin], 1, &y1.ptr.p_double[0], 1, ae_v_len(nin,nin+nout-1));
                }
                
                /*
                 * E1, Grad1
                 */
                if( hkind==0 )
                {
                    mlpgrad(&network, &x1, &y1, &v, &grad2, _state);
                }
                else
                {
                    mlpgradn(&network, &x1, &y1, &v, &grad2, _state);
                }
                e1 = e1+v;
                ae_v_add(&grad1.ptr.p_double[0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                
                /*
                 * H1
                 */
                for(j=0; j<=wcount-1; j++)
                {
                    wprev = network.weights.ptr.p_double[j];
                    network.weights.ptr.p_double[j] = wprev-2*h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad2, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad2, _state);
                    }
                    network.weights.ptr.p_double[j] = wprev-h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    ae_v_subd(&grad2.ptr.p_double[0], 1, &grad3.ptr.p_double[0], 1, ae_v_len(0,wcount-1), 8);
                    network.weights.ptr.p_double[j] = wprev+h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    ae_v_addd(&grad2.ptr.p_double[0], 1, &grad3.ptr.p_double[0], 1, ae_v_len(0,wcount-1), 8);
                    network.weights.ptr.p_double[j] = wprev+2*h;
                    if( hkind==0 )
                    {
                        mlpgrad(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    else
                    {
                        mlpgradn(&network, &x1, &y1, &v, &grad3, _state);
                    }
                    ae_v_sub(&grad2.ptr.p_double[0], 1, &grad3.ptr.p_double[0], 1, ae_v_len(0,wcount-1));
                    v = 1/(12*h);
                    ae_v_addd(&h1.ptr.pp_double[j][0], 1, &grad2.ptr.p_double[0], 1, ae_v_len(0,wcount-1), v);
                    network.weights.ptr.p_double[j] = wprev;
                }
            }
            if( hkind==0 )
            {
                mlphessianbatch(&network, &xy, ssize, &e2, &grad2, &h2, _state);
            }
            else
            {
                mlphessiannbatch(&network, &xy, ssize, &e2, &grad2, &h2, _state);
            }
            *err = *err||ae_fp_greater(ae_fabs(e1-e2, _state)/e1,etol);
            for(i=0; i<=wcount-1; i++)
            {
                if( ae_fp_greater(ae_fabs(grad1.ptr.p_double[i], _state),1.0E-2) )
                {
                    *err = *err||ae_fp_greater(ae_fabs((grad2.ptr.p_double[i]-grad1.ptr.p_double[i])/grad1.ptr.p_double[i], _state),etol);
                }
                else
                {
                    *err = *err||ae_fp_greater(ae_fabs(grad2.ptr.p_double[i]-grad1.ptr.p_double[i], _state),etol);
                }
            }
            for(i=0; i<=wcount-1; i++)
            {
                for(j=0; j<=wcount-1; j++)
                {
                    if( ae_fp_greater(ae_fabs(h1.ptr.pp_double[i][j], _state),5.0E-2) )
                    {
                        *err = *err||ae_fp_greater(ae_fabs((h1.ptr.pp_double[i][j]-h2.ptr.pp_double[i][j])/h1.ptr.pp_double[i][j], _state),etol);
                    }
                    else
                    {
                        *err = *err||ae_fp_greater(ae_fabs(h2.ptr.pp_double[i][j]-h1.ptr.pp_double[i][j], _state),etol);
                    }
                }
            }
        }
    }
    ae_frame_leave(_state);
}



static void testmlpeunit_createensemble(mlpensemble* ensemble,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_state *_state);
static void testmlpeunit_unsetensemble(mlpensemble* ensemble,
     ae_state *_state);
static void testmlpeunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);
static void testmlpeunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state);





ae_bool testmlpe(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t maxhid;
    ae_int_t nf;
    ae_int_t nhid;
    ae_int_t nl;
    ae_int_t nhid1;
    ae_int_t nhid2;
    ae_int_t ec;
    ae_int_t nkind;
    ae_int_t algtype;
    ae_int_t tasktype;
    ae_int_t pass;
    mlpensemble ensemble;
    mlpreport rep;
    mlpcvreport oobrep;
    ae_matrix xy;
    ae_int_t i;
    ae_int_t j;
    ae_int_t nin;
    ae_int_t nout;
    ae_int_t npoints;
    double e;
    ae_int_t info;
    ae_int_t nless;
    ae_int_t nall;
    ae_int_t nclasses;
    ae_bool inferrors;
    ae_bool procerrors;
    ae_bool trnerrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    _mlpensemble_init(&ensemble, _state, ae_true);
    _mlpreport_init(&rep, _state, ae_true);
    _mlpcvreport_init(&oobrep, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    inferrors = ae_false;
    procerrors = ae_false;
    trnerrors = ae_false;
    passcount = 10;
    maxn = 4;
    maxhid = 4;
    
    /*
     * General MLP ensembles tests
     */
    for(nf=1; nf<=maxn; nf++)
    {
        for(nl=1; nl<=maxn; nl++)
        {
            for(nhid1=0; nhid1<=maxhid; nhid1++)
            {
                for(nhid2=0; nhid2<=0; nhid2++)
                {
                    for(nkind=0; nkind<=3; nkind++)
                    {
                        for(ec=1; ec<=3; ec++)
                        {
                            
                            /*
                             *  Skip meaningless parameters combinations
                             */
                            if( nkind==1&&nl<2 )
                            {
                                continue;
                            }
                            if( nhid1==0&&nhid2!=0 )
                            {
                                continue;
                            }
                            
                            /*
                             * Tests
                             */
                            testmlpeunit_testinformational(nkind, nf, nhid1, nhid2, nl, ec, passcount, &inferrors, _state);
                            testmlpeunit_testprocessing(nkind, nf, nhid1, nhid2, nl, ec, passcount, &procerrors, _state);
                        }
                    }
                }
            }
        }
    }
    
    /*
     * network training must reduce error
     * test on random regression task
     */
    nin = 3;
    nout = 2;
    nhid = 5;
    npoints = 100;
    nless = 0;
    nall = 0;
    for(pass=1; pass<=10; pass++)
    {
        for(algtype=0; algtype<=1; algtype++)
        {
            for(tasktype=0; tasktype<=1; tasktype++)
            {
                if( tasktype==0 )
                {
                    ae_matrix_set_length(&xy, npoints-1+1, nin+nout-1+1, _state);
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin+nout-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    mlpecreate1(nin, nhid, nout, 1+ae_randominteger(3, _state), &ensemble, _state);
                }
                else
                {
                    ae_matrix_set_length(&xy, npoints-1+1, nin+1, _state);
                    nclasses = 2+ae_randominteger(2, _state);
                    for(i=0; i<=npoints-1; i++)
                    {
                        for(j=0; j<=nin-1; j++)
                        {
                            xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                        xy.ptr.pp_double[i][nin] = ae_randominteger(nclasses, _state);
                    }
                    mlpecreatec1(nin, nhid, nclasses, 1+ae_randominteger(3, _state), &ensemble, _state);
                }
                e = mlpermserror(&ensemble, &xy, npoints, _state);
                if( algtype==0 )
                {
                    mlpebagginglm(&ensemble, &xy, npoints, 0.001, 1, &info, &rep, &oobrep, _state);
                }
                else
                {
                    mlpebagginglbfgs(&ensemble, &xy, npoints, 0.001, 1, 0.01, 0, &info, &rep, &oobrep, _state);
                }
                if( info<0 )
                {
                    trnerrors = ae_true;
                }
                else
                {
                    if( ae_fp_less(mlpermserror(&ensemble, &xy, npoints, _state),e) )
                    {
                        nless = nless+1;
                    }
                }
                nall = nall+1;
            }
        }
    }
    trnerrors = trnerrors||ae_fp_greater(nall-nless,0.3*nall);
    
    /*
     * Final report
     */
    waserrors = (inferrors||procerrors)||trnerrors;
    if( !silent )
    {
        printf("MLP ENSEMBLE TEST\n");
        printf("INFORMATIONAL FUNCTIONS:                 ");
        if( !inferrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("BASIC PROCESSING:                        ");
        if( !procerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("TRAINING:                                ");
        if( !trnerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Network creation
*************************************************************************/
static void testmlpeunit_createensemble(mlpensemble* ensemble,
     ae_int_t nkind,
     double a1,
     double a2,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_state *_state)
{


    ae_assert(((nin>0&&nhid1>=0)&&nhid2>=0)&&nout>0, "CreateNetwork error", _state);
    ae_assert(nhid1!=0||nhid2==0, "CreateNetwork error", _state);
    ae_assert(nkind!=1||nout>=2, "CreateNetwork error", _state);
    if( nhid1==0 )
    {
        
        /*
         * No hidden layers
         */
        if( nkind==0 )
        {
            mlpecreate0(nin, nout, ec, ensemble, _state);
        }
        else
        {
            if( nkind==1 )
            {
                mlpecreatec0(nin, nout, ec, ensemble, _state);
            }
            else
            {
                if( nkind==2 )
                {
                    mlpecreateb0(nin, nout, a1, a2, ec, ensemble, _state);
                }
                else
                {
                    if( nkind==3 )
                    {
                        mlpecreater0(nin, nout, a1, a2, ec, ensemble, _state);
                    }
                }
            }
        }
        return;
    }
    if( nhid2==0 )
    {
        
        /*
         * One hidden layer
         */
        if( nkind==0 )
        {
            mlpecreate1(nin, nhid1, nout, ec, ensemble, _state);
        }
        else
        {
            if( nkind==1 )
            {
                mlpecreatec1(nin, nhid1, nout, ec, ensemble, _state);
            }
            else
            {
                if( nkind==2 )
                {
                    mlpecreateb1(nin, nhid1, nout, a1, a2, ec, ensemble, _state);
                }
                else
                {
                    if( nkind==3 )
                    {
                        mlpecreater1(nin, nhid1, nout, a1, a2, ec, ensemble, _state);
                    }
                }
            }
        }
        return;
    }
    
    /*
     * Two hidden layers
     */
    if( nkind==0 )
    {
        mlpecreate2(nin, nhid1, nhid2, nout, ec, ensemble, _state);
    }
    else
    {
        if( nkind==1 )
        {
            mlpecreatec2(nin, nhid1, nhid2, nout, ec, ensemble, _state);
        }
        else
        {
            if( nkind==2 )
            {
                mlpecreateb2(nin, nhid1, nhid2, nout, a1, a2, ec, ensemble, _state);
            }
            else
            {
                if( nkind==3 )
                {
                    mlpecreater2(nin, nhid1, nhid2, nout, a1, a2, ec, ensemble, _state);
                }
            }
        }
    }
}


/*************************************************************************
Unsets network (initialize it to smallest network possible
*************************************************************************/
static void testmlpeunit_unsetensemble(mlpensemble* ensemble,
     ae_state *_state)
{


    mlpecreate0(1, 1, 1, ensemble, _state);
}


/*************************************************************************
Iformational functions test
*************************************************************************/
static void testmlpeunit_testinformational(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    mlpensemble ensemble;
    ae_int_t n1;
    ae_int_t n2;

    ae_frame_make(_state, &_frame_block);
    _mlpensemble_init(&ensemble, _state, ae_true);

    testmlpeunit_createensemble(&ensemble, nkind, -1.0, 1.0, nin, nhid1, nhid2, nout, ec, _state);
    mlpeproperties(&ensemble, &n1, &n2, _state);
    *err = (*err||n1!=nin)||n2!=nout;
    ae_frame_leave(_state);
}


/*************************************************************************
Processing functions test
*************************************************************************/
static void testmlpeunit_testprocessing(ae_int_t nkind,
     ae_int_t nin,
     ae_int_t nhid1,
     ae_int_t nhid2,
     ae_int_t nout,
     ae_int_t ec,
     ae_int_t passcount,
     ae_bool* err,
     ae_state *_state)
{
    ae_frame _frame_block;
    mlpensemble ensemble;
    mlpensemble ensemble2;
    double a1;
    double a2;
    ae_int_t pass;
    ae_int_t i;
    ae_bool allsame;
    ae_int_t rlen;
    ae_vector x1;
    ae_vector x2;
    ae_vector y1;
    ae_vector y2;
    ae_vector ra;
    ae_vector ra2;
    double v;

    ae_frame_make(_state, &_frame_block);
    _mlpensemble_init(&ensemble, _state, ae_true);
    _mlpensemble_init(&ensemble2, _state, ae_true);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ra, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ra2, 0, DT_REAL, _state, ae_true);

    
    /*
     * Prepare network
     */
    a1 = 0;
    a2 = 0;
    if( nkind==2 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = 2*ae_randomreal(_state)-1;
    }
    if( nkind==3 )
    {
        a1 = 1000*ae_randomreal(_state)-500;
        a2 = a1+(2*ae_randominteger(2, _state)-1)*(0.1+0.9*ae_randomreal(_state));
    }
    
    /*
     * Initialize arrays
     */
    ae_vector_set_length(&x1, nin-1+1, _state);
    ae_vector_set_length(&x2, nin-1+1, _state);
    ae_vector_set_length(&y1, nout-1+1, _state);
    ae_vector_set_length(&y2, nout-1+1, _state);
    
    /*
     * Main cycle
     */
    for(pass=1; pass<=passcount; pass++)
    {
        testmlpeunit_createensemble(&ensemble, nkind, a1, a2, nin, nhid1, nhid2, nout, ec, _state);
        
        /*
         * Same inputs leads to same outputs
         */
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpeprocess(&ensemble, &x1, &y1, _state);
        mlpeprocess(&ensemble, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original network leads to same outputs
         * on copy created using MLPCopy
         */
        testmlpeunit_unsetensemble(&ensemble2, _state);
        mlpecopy(&ensemble, &ensemble2, _state);
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpeprocess(&ensemble, &x1, &y1, _state);
        mlpeprocess(&ensemble2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Same inputs on original network leads to same outputs
         * on copy created using MLPSerialize
         */
        testmlpeunit_unsetensemble(&ensemble2, _state);
        mlpeserialize(&ensemble, &ra, &rlen, _state);
        ae_vector_set_length(&ra2, rlen-1+1, _state);
        for(i=0; i<=rlen-1; i++)
        {
            ra2.ptr.p_double[i] = ra.ptr.p_double[i];
        }
        mlpeunserialize(&ra2, &ensemble2, _state);
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = x1.ptr.p_double[i];
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        mlpeprocess(&ensemble, &x1, &y1, _state);
        mlpeprocess(&ensemble2, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||!allsame;
        
        /*
         * Different inputs leads to different outputs (non-zero network)
         */
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = y1.ptr.p_double[i];
        }
        mlpeprocess(&ensemble, &x1, &y1, _state);
        mlpeprocess(&ensemble, &x2, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||allsame;
        
        /*
         * Randomization changes outputs (when inputs are unchanged, non-zero network)
         */
        for(i=0; i<=nin-1; i++)
        {
            x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            x2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=nout-1; i++)
        {
            y1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            y2.ptr.p_double[i] = y1.ptr.p_double[i];
        }
        mlpecopy(&ensemble, &ensemble2, _state);
        mlperandomize(&ensemble2, _state);
        mlpeprocess(&ensemble, &x1, &y1, _state);
        mlpeprocess(&ensemble2, &x1, &y2, _state);
        allsame = ae_true;
        for(i=0; i<=nout-1; i++)
        {
            allsame = allsame&&ae_fp_eq(y1.ptr.p_double[i],y2.ptr.p_double[i]);
        }
        *err = *err||allsame;
        
        /*
         * Normalization properties
         */
        if( nkind==1 )
        {
            
            /*
             * Classifier network outputs are normalized
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpeprocess(&ensemble, &x1, &y1, _state);
            v = 0;
            for(i=0; i<=nout-1; i++)
            {
                v = v+y1.ptr.p_double[i];
                *err = *err||ae_fp_less(y1.ptr.p_double[i],0);
            }
            *err = *err||ae_fp_greater(ae_fabs(v-1, _state),1000*ae_machineepsilon);
        }
        if( nkind==2 )
        {
            
            /*
             * B-type network outputs are bounded from above/below
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpeprocess(&ensemble, &x1, &y1, _state);
            for(i=0; i<=nout-1; i++)
            {
                if( ae_fp_greater_eq(a2,0) )
                {
                    *err = *err||ae_fp_less(y1.ptr.p_double[i],a1);
                }
                else
                {
                    *err = *err||ae_fp_greater(y1.ptr.p_double[i],a1);
                }
            }
        }
        if( nkind==3 )
        {
            
            /*
             * R-type network outputs are within [A1,A2] (or [A2,A1])
             */
            for(i=0; i<=nin-1; i++)
            {
                x1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            mlpeprocess(&ensemble, &x1, &y1, _state);
            for(i=0; i<=nout-1; i++)
            {
                *err = (*err||ae_fp_less(y1.ptr.p_double[i],ae_minreal(a1, a2, _state)))||ae_fp_greater(y1.ptr.p_double[i],ae_maxreal(a1, a2, _state));
            }
        }
    }
    ae_frame_leave(_state);
}



static void testpcaunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state);





ae_bool testpca(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t maxm;
    double threshold;
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t info;
    ae_vector means;
    ae_vector s;
    ae_vector t2;
    ae_vector t3;
    ae_matrix v;
    ae_matrix x;
    double t;
    double h;
    double tmean;
    double tmeans;
    double tstddev;
    double tstddevs;
    double tmean2;
    double tmeans2;
    double tstddev2;
    double tstddevs2;
    ae_bool pcaconverrors;
    ae_bool pcaorterrors;
    ae_bool pcavarerrors;
    ae_bool pcaopterrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&means, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t3, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&x, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Primary settings
     */
    maxm = 10;
    maxn = 100;
    passcount = 1;
    threshold = 1000*ae_machineepsilon;
    waserrors = ae_false;
    pcaconverrors = ae_false;
    pcaorterrors = ae_false;
    pcavarerrors = ae_false;
    pcaopterrors = ae_false;
    
    /*
     * Test 1: N random points in M-dimensional space
     */
    for(m=1; m<=maxm; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Generate task
             */
            ae_matrix_set_length(&x, n-1+1, m-1+1, _state);
            ae_vector_set_length(&means, m-1+1, _state);
            for(j=0; j<=m-1; j++)
            {
                means.ptr.p_double[j] = 1.5*ae_randomreal(_state)-0.75;
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    x.ptr.pp_double[i][j] = means.ptr.p_double[j]+(2*ae_randomreal(_state)-1);
                }
            }
            
            /*
             * Solve
             */
            pcabuildbasis(&x, n, m, &info, &s, &v, _state);
            if( info!=1 )
            {
                pcaconverrors = ae_true;
                continue;
            }
            
            /*
             * Orthogonality test
             */
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    t = ae_v_dotproduct(&v.ptr.pp_double[0][i], v.stride, &v.ptr.pp_double[0][j], v.stride, ae_v_len(0,m-1));
                    if( i==j )
                    {
                        t = t-1;
                    }
                    pcaorterrors = pcaorterrors||ae_fp_greater(ae_fabs(t, _state),threshold);
                }
            }
            
            /*
             * Variance test
             */
            ae_vector_set_length(&t2, n-1+1, _state);
            for(k=0; k<=m-1; k++)
            {
                for(i=0; i<=n-1; i++)
                {
                    t = ae_v_dotproduct(&x.ptr.pp_double[i][0], 1, &v.ptr.pp_double[0][k], v.stride, ae_v_len(0,m-1));
                    t2.ptr.p_double[i] = t;
                }
                testpcaunit_calculatemv(&t2, n, &tmean, &tmeans, &tstddev, &tstddevs, _state);
                if( n!=1 )
                {
                    t = ae_sqr(tstddev, _state)*n/(n-1);
                }
                else
                {
                    t = 0;
                }
                pcavarerrors = pcavarerrors||ae_fp_greater(ae_fabs(t-s.ptr.p_double[k], _state),threshold);
            }
            for(k=0; k<=m-2; k++)
            {
                pcavarerrors = pcavarerrors||ae_fp_less(s.ptr.p_double[k],s.ptr.p_double[k+1]);
            }
            
            /*
             * Optimality: different perturbations in V[..,0] can't
             * increase variance of projection - can only decrease.
             */
            ae_vector_set_length(&t2, n-1+1, _state);
            ae_vector_set_length(&t3, n-1+1, _state);
            for(i=0; i<=n-1; i++)
            {
                t = ae_v_dotproduct(&x.ptr.pp_double[i][0], 1, &v.ptr.pp_double[0][0], v.stride, ae_v_len(0,m-1));
                t2.ptr.p_double[i] = t;
            }
            testpcaunit_calculatemv(&t2, n, &tmean, &tmeans, &tstddev, &tstddevs, _state);
            for(k=0; k<=2*m-1; k++)
            {
                h = 0.001;
                if( k%2!=0 )
                {
                    h = -h;
                }
                ae_v_move(&t3.ptr.p_double[0], 1, &t2.ptr.p_double[0], 1, ae_v_len(0,n-1));
                ae_v_addd(&t3.ptr.p_double[0], 1, &x.ptr.pp_double[0][k/2], x.stride, ae_v_len(0,n-1), h);
                t = 0;
                for(j=0; j<=m-1; j++)
                {
                    if( j!=k/2 )
                    {
                        t = t+ae_sqr(v.ptr.pp_double[j][0], _state);
                    }
                    else
                    {
                        t = t+ae_sqr(v.ptr.pp_double[j][0]+h, _state);
                    }
                }
                t = 1/ae_sqrt(t, _state);
                ae_v_muld(&t3.ptr.p_double[0], 1, ae_v_len(0,n-1), t);
                testpcaunit_calculatemv(&t3, n, &tmean2, &tmeans2, &tstddev2, &tstddevs2, _state);
                pcaopterrors = pcaopterrors||ae_fp_greater(tstddev2,tstddev+threshold);
            }
        }
    }
    
    /*
     * Special test for N=0
     */
    for(m=1; m<=maxm; m++)
    {
        
        /*
         * Solve
         */
        pcabuildbasis(&x, 0, m, &info, &s, &v, _state);
        if( info!=1 )
        {
            pcaconverrors = ae_true;
            continue;
        }
        
        /*
         * Orthogonality test
         */
        for(i=0; i<=m-1; i++)
        {
            for(j=0; j<=m-1; j++)
            {
                t = ae_v_dotproduct(&v.ptr.pp_double[0][i], v.stride, &v.ptr.pp_double[0][j], v.stride, ae_v_len(0,m-1));
                if( i==j )
                {
                    t = t-1;
                }
                pcaorterrors = pcaorterrors||ae_fp_greater(ae_fabs(t, _state),threshold);
            }
        }
    }
    
    /*
     * Final report
     */
    waserrors = ((pcaconverrors||pcaorterrors)||pcavarerrors)||pcaopterrors;
    if( !silent )
    {
        printf("PCA TEST\n");
        printf("TOTAL RESULTS:                           ");
        if( !waserrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* CONVERGENCE                            ");
        if( !pcaconverrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* ORTOGONALITY                           ");
        if( !pcaorterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* VARIANCE REPORT                        ");
        if( !pcavarerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("* OPTIMALITY                             ");
        if( !pcaopterrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST SUMMARY: FAILED\n");
        }
        else
        {
            printf("TEST SUMMARY: PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Moments estimates and their errors
*************************************************************************/
static void testpcaunit_calculatemv(/* Real    */ ae_vector* x,
     ae_int_t n,
     double* mean,
     double* means,
     double* stddev,
     double* stddevs,
     ae_state *_state)
{
    ae_int_t i;
    double v1;
    double v2;
    double variance;

    *mean = 0;
    *means = 0;
    *stddev = 0;
    *stddevs = 0;

    *mean = 0;
    *means = 1;
    *stddev = 0;
    *stddevs = 1;
    variance = 0;
    if( n<=1 )
    {
        return;
    }
    
    /*
     * Mean
     */
    for(i=0; i<=n-1; i++)
    {
        *mean = *mean+x->ptr.p_double[i];
    }
    *mean = *mean/n;
    
    /*
     * Variance (using corrected two-pass algorithm)
     */
    if( n!=1 )
    {
        v1 = 0;
        for(i=0; i<=n-1; i++)
        {
            v1 = v1+ae_sqr(x->ptr.p_double[i]-(*mean), _state);
        }
        v2 = 0;
        for(i=0; i<=n-1; i++)
        {
            v2 = v2+(x->ptr.p_double[i]-(*mean));
        }
        v2 = ae_sqr(v2, _state)/n;
        variance = (v1-v2)/n;
        if( ae_fp_less(variance,0) )
        {
            variance = 0;
        }
        *stddev = ae_sqrt(variance, _state);
    }
    
    /*
     * Errors
     */
    *means = *stddev/ae_sqrt(n, _state);
    *stddevs = *stddev*ae_sqrt(2, _state)/ae_sqrt(n-1, _state);
}



static void testodesolverunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state);
static void testodesolverunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state);
static void testodesolverunit_unsetrep(odesolverreport* rep,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testodesolver(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t passcount;
    ae_bool curerrors;
    ae_bool rkckerrors;
    ae_bool waserrors;
    ae_vector xtbl;
    ae_matrix ytbl;
    odesolverreport rep;
    ae_vector xg;
    ae_vector y;
    double h;
    double eps;
    ae_int_t solver;
    ae_int_t pass;
    ae_int_t mynfev;
    double v;
    ae_int_t m;
    ae_int_t m2;
    ae_int_t i;
    double err;
    odesolverstate state;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&xtbl, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ytbl, 0, 0, DT_REAL, _state, ae_true);
    _odesolverreport_init(&rep, _state, ae_true);
    ae_vector_init(&xg, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _odesolverstate_init(&state, _state, ae_true);

    rkckerrors = ae_false;
    waserrors = ae_false;
    passcount = 10;
    
    /*
     * simple test: just A*sin(x)+B*cos(x)
     */
    ae_assert(passcount>=2, "Assertion failed", _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        for(solver=0; solver<=0; solver++)
        {
            
            /*
             * prepare
             */
            h = 1.0E-2;
            eps = 1.0E-5;
            if( pass%2==0 )
            {
                eps = -eps;
            }
            ae_vector_set_length(&y, 2, _state);
            for(i=0; i<=1; i++)
            {
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            m = 2+ae_randominteger(10, _state);
            ae_vector_set_length(&xg, m, _state);
            xg.ptr.p_double[0] = (m-1)*ae_randomreal(_state);
            for(i=1; i<=m-1; i++)
            {
                xg.ptr.p_double[i] = xg.ptr.p_double[i-1]+ae_randomreal(_state);
            }
            v = 2*ae_pi/(xg.ptr.p_double[m-1]-xg.ptr.p_double[0]);
            ae_v_muld(&xg.ptr.p_double[0], 1, ae_v_len(0,m-1), v);
            if( ae_fp_greater(ae_randomreal(_state),0.5) )
            {
                ae_v_muld(&xg.ptr.p_double[0], 1, ae_v_len(0,m-1), -1);
            }
            mynfev = 0;
            
            /*
             * choose solver
             */
            if( solver==0 )
            {
                odesolverrkck(&y, 2, &xg, m, eps, h, &state, _state);
            }
            
            /*
             * solve
             */
            while(odesolveriteration(&state, _state))
            {
                state.dy.ptr.p_double[0] = state.y.ptr.p_double[1];
                state.dy.ptr.p_double[1] = -state.y.ptr.p_double[0];
                mynfev = mynfev+1;
            }
            odesolverresults(&state, &m2, &xtbl, &ytbl, &rep, _state);
            
            /*
             * check results
             */
            curerrors = ae_false;
            if( rep.terminationtype<=0 )
            {
                curerrors = ae_true;
            }
            else
            {
                curerrors = curerrors||m2!=m;
                err = 0;
                for(i=0; i<=m-1; i++)
                {
                    err = ae_maxreal(err, ae_fabs(ytbl.ptr.pp_double[i][0]-(y.ptr.p_double[0]*ae_cos(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)+y.ptr.p_double[1]*ae_sin(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)), _state), _state);
                    err = ae_maxreal(err, ae_fabs(ytbl.ptr.pp_double[i][1]-(-y.ptr.p_double[0]*ae_sin(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)+y.ptr.p_double[1]*ae_cos(xtbl.ptr.p_double[i]-xtbl.ptr.p_double[0], _state)), _state), _state);
                }
                curerrors = curerrors||ae_fp_greater(err,10*ae_fabs(eps, _state));
                curerrors = curerrors||mynfev!=rep.nfev;
            }
            if( solver==0 )
            {
                rkckerrors = rkckerrors||curerrors;
            }
        }
    }
    
    /*
     * another test:
     *
     *     y(0)   = 0
     *     dy/dx  = f(x,y)
     *     f(x,y) = 0,   x<1
     *              x-1, x>=1
     *
     * with BOTH absolute and fractional tolerances.
     * Starting from zero will be real challenge for
     * fractional tolerance.
     */
    ae_assert(passcount>=2, "Assertion failed", _state);
    for(pass=0; pass<=passcount-1; pass++)
    {
        h = 1.0E-4;
        eps = 1.0E-4;
        if( pass%2==0 )
        {
            eps = -eps;
        }
        ae_vector_set_length(&y, 1, _state);
        y.ptr.p_double[0] = 0;
        m = 21;
        ae_vector_set_length(&xg, m, _state);
        for(i=0; i<=m-1; i++)
        {
            xg.ptr.p_double[i] = (double)(2*i)/(double)(m-1);
        }
        mynfev = 0;
        odesolverrkck(&y, 1, &xg, m, eps, h, &state, _state);
        while(odesolveriteration(&state, _state))
        {
            state.dy.ptr.p_double[0] = ae_maxreal(state.x-1, 0, _state);
            mynfev = mynfev+1;
        }
        odesolverresults(&state, &m2, &xtbl, &ytbl, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            rkckerrors = ae_true;
        }
        else
        {
            rkckerrors = rkckerrors||m2!=m;
            err = 0;
            for(i=0; i<=m-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(ytbl.ptr.pp_double[i][0]-ae_sqr(ae_maxreal(xg.ptr.p_double[i]-1, 0, _state), _state)/2, _state), _state);
            }
            rkckerrors = rkckerrors||ae_fp_greater(err,ae_fabs(eps, _state));
            rkckerrors = rkckerrors||mynfev!=rep.nfev;
        }
    }
    
    /*
     * end
     */
    waserrors = rkckerrors;
    if( !silent )
    {
        printf("TESTING ODE SOLVER\n");
        printf("* RK CASH-KARP:                           ");
        if( rkckerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets real matrix
*************************************************************************/
static void testodesolverunit_unset2d(/* Real    */ ae_matrix* x,
     ae_state *_state)
{


    ae_matrix_set_length(x, 1, 1, _state);
    x->ptr.pp_double[0][0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testodesolverunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Unsets report
*************************************************************************/
static void testodesolverunit_unsetrep(odesolverreport* rep,
     ae_state *_state)
{


    rep->nfev = 0;
}



static void testfftunit_reffftc1d(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);
static void testfftunit_reffftc1dinv(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);
static void testfftunit_refinternalcfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     ae_bool inversefft,
     ae_state *_state);
static void testfftunit_refinternalrfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     /* Complex */ ae_vector* f,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testfft(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    ae_vector a1;
    ae_vector a2;
    ae_vector a3;
    ae_vector r1;
    ae_vector r2;
    ae_vector buf;
    ftplan plan;
    ae_int_t maxn;
    double bidierr;
    double bidirerr;
    double referr;
    double refrerr;
    double reinterr;
    double errtol;
    ae_bool referrors;
    ae_bool bidierrors;
    ae_bool refrerrors;
    ae_bool bidirerrors;
    ae_bool reinterrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&a1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&a2, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&a3, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&r1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);
    _ftplan_init(&plan, _state, ae_true);

    maxn = 128;
    errtol = 100000*ae_pow(maxn, (double)3/(double)2, _state)*ae_machineepsilon;
    bidierrors = ae_false;
    referrors = ae_false;
    bidirerrors = ae_false;
    refrerrors = ae_false;
    reinterrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test bi-directional error: norm(x-invFFT(FFT(x)))
     */
    bidierr = 0;
    bidirerr = 0;
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * Complex FFT/invFFT
         */
        ae_vector_set_length(&a1, n, _state);
        ae_vector_set_length(&a2, n, _state);
        ae_vector_set_length(&a3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            a1.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            a2.ptr.p_complex[i] = a1.ptr.p_complex[i];
            a3.ptr.p_complex[i] = a1.ptr.p_complex[i];
        }
        fftc1d(&a2, n, _state);
        fftc1dinv(&a2, n, _state);
        fftc1dinv(&a3, n, _state);
        fftc1d(&a3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            bidierr = ae_maxreal(bidierr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
            bidierr = ae_maxreal(bidierr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a3.ptr.p_complex[i]), _state), _state);
        }
        
        /*
         * Real
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fftr1d(&r2, n, &a1, _state);
        ae_v_muld(&r2.ptr.p_double[0], 1, ae_v_len(0,n-1), 0);
        fftr1dinv(&a1, n, &r2, _state);
        for(i=0; i<=n-1; i++)
        {
            bidirerr = ae_maxreal(bidirerr, ae_c_abs(ae_complex_from_d(r1.ptr.p_double[i]-r2.ptr.p_double[i]), _state), _state);
        }
    }
    bidierrors = bidierrors||ae_fp_greater(bidierr,errtol);
    bidirerrors = bidirerrors||ae_fp_greater(bidirerr,errtol);
    
    /*
     * Test against reference O(N^2) implementation
     */
    referr = 0;
    refrerr = 0;
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * Complex FFT
         */
        ae_vector_set_length(&a1, n, _state);
        ae_vector_set_length(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            a1.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            a2.ptr.p_complex[i] = a1.ptr.p_complex[i];
        }
        fftc1d(&a1, n, _state);
        testfftunit_reffftc1d(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
        }
        
        /*
         * Complex inverse FFT
         */
        ae_vector_set_length(&a1, n, _state);
        ae_vector_set_length(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            a1.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
            a1.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            a2.ptr.p_complex[i] = a1.ptr.p_complex[i];
        }
        fftc1dinv(&a1, n, _state);
        testfftunit_reffftc1dinv(&a2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
        }
        
        /*
         * Real forward/inverse FFT:
         * * calculate and check forward FFT
         * * use precalculated FFT to check backward FFT
         *   fill unused parts of frequencies array with random numbers
         *   to ensure that they are not really used
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fftr1d(&r1, n, &a1, _state);
        testfftunit_refinternalrfft(&r2, n, &a2, _state);
        for(i=0; i<=n-1; i++)
        {
            refrerr = ae_maxreal(refrerr, ae_c_abs(ae_c_sub(a1.ptr.p_complex[i],a2.ptr.p_complex[i]), _state), _state);
        }
        ae_vector_set_length(&a3, ae_ifloor((double)n/(double)2, _state)+1, _state);
        for(i=0; i<=ae_ifloor((double)n/(double)2, _state); i++)
        {
            a3.ptr.p_complex[i] = a2.ptr.p_complex[i];
        }
        a3.ptr.p_complex[0].y = 2*ae_randomreal(_state)-1;
        if( n%2==0 )
        {
            a3.ptr.p_complex[ae_ifloor((double)n/(double)2, _state)].y = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 0;
        }
        fftr1dinv(&a3, n, &r1, _state);
        for(i=0; i<=n-1; i++)
        {
            refrerr = ae_maxreal(refrerr, ae_fabs(r2.ptr.p_double[i]-r1.ptr.p_double[i], _state), _state);
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    refrerrors = refrerrors||ae_fp_greater(refrerr,errtol);
    
    /*
     * test internal real even FFT
     */
    reinterr = 0;
    for(k=1; k<=maxn/2; k++)
    {
        n = 2*k;
        
        /*
         * Real forward FFT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        ftbasegeneratecomplexfftplan(n/2, &plan, _state);
        ae_vector_set_length(&buf, n, _state);
        fftr1dinternaleven(&r1, n, &buf, &plan, _state);
        testfftunit_refinternalrfft(&r2, n, &a2, _state);
        reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[0]-a2.ptr.p_complex[0].x, _state), _state);
        reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[1]-a2.ptr.p_complex[n/2].x, _state), _state);
        for(i=1; i<=n/2-1; i++)
        {
            reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[2*i+0]-a2.ptr.p_complex[i].x, _state), _state);
            reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[2*i+1]-a2.ptr.p_complex[i].y, _state), _state);
        }
        
        /*
         * Real backward FFT
         */
        ae_vector_set_length(&r1, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        ae_vector_set_length(&a2, ae_ifloor((double)n/(double)2, _state)+1, _state);
        a2.ptr.p_complex[0] = ae_complex_from_d(r1.ptr.p_double[0]);
        for(i=1; i<=ae_ifloor((double)n/(double)2, _state)-1; i++)
        {
            a2.ptr.p_complex[i].x = r1.ptr.p_double[2*i+0];
            a2.ptr.p_complex[i].y = r1.ptr.p_double[2*i+1];
        }
        a2.ptr.p_complex[ae_ifloor((double)n/(double)2, _state)] = ae_complex_from_d(r1.ptr.p_double[1]);
        ftbasegeneratecomplexfftplan(n/2, &plan, _state);
        ae_vector_set_length(&buf, n, _state);
        fftr1dinvinternaleven(&r1, n, &buf, &plan, _state);
        fftr1dinv(&a2, n, &r2, _state);
        for(i=0; i<=n-1; i++)
        {
            reinterr = ae_maxreal(reinterr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
        }
    }
    reinterrors = reinterrors||ae_fp_greater(reinterr,errtol);
    
    /*
     * end
     */
    waserrors = (((bidierrors||bidirerrors)||referrors)||refrerrors)||reinterrors;
    if( !silent )
    {
        printf("TESTING FFT\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BI-DIRECTIONAL COMPLEX TEST:            ");
        if( bidierrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE COMPLEX FFT:          ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BI-DIRECTIONAL REAL TEST:               ");
        if( bidirerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE REAL FFT:             ");
        if( refrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* INTERNAL EVEN FFT:                      ");
        if( reinterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference FFT
*************************************************************************/
static void testfftunit_reffftc1d(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector buf;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    ae_assert(n>0, "FFTC1D: incorrect N!", _state);
    ae_vector_set_length(&buf, 2*n, _state);
    for(i=0; i<=n-1; i++)
    {
        buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x;
        buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y;
    }
    testfftunit_refinternalcfft(&buf, n, ae_false, _state);
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_complex[i].x = buf.ptr.p_double[2*i+0];
        a->ptr.p_complex[i].y = buf.ptr.p_double[2*i+1];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference inverse FFT
*************************************************************************/
static void testfftunit_reffftc1dinv(/* Complex */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector buf;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    ae_assert(n>0, "FFTC1DInv: incorrect N!", _state);
    ae_vector_set_length(&buf, 2*n, _state);
    for(i=0; i<=n-1; i++)
    {
        buf.ptr.p_double[2*i+0] = a->ptr.p_complex[i].x;
        buf.ptr.p_double[2*i+1] = a->ptr.p_complex[i].y;
    }
    testfftunit_refinternalcfft(&buf, n, ae_true, _state);
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_complex[i].x = buf.ptr.p_double[2*i+0];
        a->ptr.p_complex[i].y = buf.ptr.p_double[2*i+1];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Internal complex FFT stub.
Uses straightforward formula with O(N^2) complexity.
*************************************************************************/
static void testfftunit_refinternalcfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     ae_bool inversefft,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector tmp;
    ae_int_t i;
    ae_int_t k;
    double hre;
    double him;
    double c;
    double s;
    double re;
    double im;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&tmp, 2*nn-1+1, _state);
    if( !inversefft )
    {
        for(i=0; i<=nn-1; i++)
        {
            hre = 0;
            him = 0;
            for(k=0; k<=nn-1; k++)
            {
                re = a->ptr.p_double[2*k];
                im = a->ptr.p_double[2*k+1];
                c = ae_cos(-2*ae_pi*k*i/nn, _state);
                s = ae_sin(-2*ae_pi*k*i/nn, _state);
                hre = hre+c*re-s*im;
                him = him+c*im+s*re;
            }
            tmp.ptr.p_double[2*i] = hre;
            tmp.ptr.p_double[2*i+1] = him;
        }
        for(i=0; i<=2*nn-1; i++)
        {
            a->ptr.p_double[i] = tmp.ptr.p_double[i];
        }
    }
    else
    {
        for(k=0; k<=nn-1; k++)
        {
            hre = 0;
            him = 0;
            for(i=0; i<=nn-1; i++)
            {
                re = a->ptr.p_double[2*i];
                im = a->ptr.p_double[2*i+1];
                c = ae_cos(2*ae_pi*k*i/nn, _state);
                s = ae_sin(2*ae_pi*k*i/nn, _state);
                hre = hre+c*re-s*im;
                him = him+c*im+s*re;
            }
            tmp.ptr.p_double[2*k] = hre/nn;
            tmp.ptr.p_double[2*k+1] = him/nn;
        }
        for(i=0; i<=2*nn-1; i++)
        {
            a->ptr.p_double[i] = tmp.ptr.p_double[i];
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Internal real FFT stub.
Uses straightforward formula with O(N^2) complexity.
*************************************************************************/
static void testfftunit_refinternalrfft(/* Real    */ ae_vector* a,
     ae_int_t nn,
     /* Complex */ ae_vector* f,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector tmp;
    ae_int_t i;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(f);
    ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&tmp, 2*nn-1+1, _state);
    for(i=0; i<=nn-1; i++)
    {
        tmp.ptr.p_double[2*i] = a->ptr.p_double[i];
        tmp.ptr.p_double[2*i+1] = 0;
    }
    testfftunit_refinternalcfft(&tmp, nn, ae_false, _state);
    ae_vector_set_length(f, nn, _state);
    for(i=0; i<=nn-1; i++)
    {
        f->ptr.p_complex[i].x = tmp.ptr.p_double[2*i+0];
        f->ptr.p_complex[i].y = tmp.ptr.p_double[2*i+1];
    }
    ae_frame_leave(_state);
}



static void testconvunit_refconvc1d(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testconvunit_refconvc1dcircular(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testconvunit_refconvr1d(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state);
static void testconvunit_refconvr1dcircular(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testconv(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_int_t rkind;
    ae_int_t circkind;
    ae_vector ra;
    ae_vector rb;
    ae_vector rr1;
    ae_vector rr2;
    ae_vector ca;
    ae_vector cb;
    ae_vector cr1;
    ae_vector cr2;
    ae_int_t maxn;
    double referr;
    double refrerr;
    double inverr;
    double invrerr;
    double errtol;
    ae_bool referrors;
    ae_bool refrerrors;
    ae_bool inverrors;
    ae_bool invrerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&ra, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ca, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cb, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr2, 0, DT_COMPLEX, _state, ae_true);

    maxn = 32;
    errtol = 100000*ae_pow(maxn, (double)3/(double)2, _state)*ae_machineepsilon;
    referrors = ae_false;
    refrerrors = ae_false;
    inverrors = ae_false;
    invrerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test against reference O(N^2) implementation.
     *
     * Automatic ConvC1D() and different algorithms of ConvC1DX() are tested.
     */
    referr = 0;
    refrerr = 0;
    for(m=1; m<=maxn; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(circkind=0; circkind<=1; circkind++)
            {
                for(rkind=-3; rkind<=1; rkind++)
                {
                    
                    /*
                     * skip impossible combinations of parameters:
                     * * circular convolution, M<N, RKind<>-3 - internal subroutine does not support M<N.
                     */
                    if( (circkind!=0&&m<n)&&rkind!=-3 )
                    {
                        continue;
                    }
                    
                    /*
                     * Complex convolution
                     */
                    ae_vector_set_length(&ca, m, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        ca.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                        ca.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&cb, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        cb.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                        cb.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&cr1, 1, _state);
                    if( rkind==-3 )
                    {
                        
                        /*
                         * test wrapper subroutine:
                         * * circular/non-circular
                         */
                        if( circkind==0 )
                        {
                            convc1d(&ca, m, &cb, n, &cr1, _state);
                        }
                        else
                        {
                            convc1dcircular(&ca, m, &cb, n, &cr1, _state);
                        }
                    }
                    else
                    {
                        
                        /*
                         * test internal subroutine
                         */
                        if( m>=n )
                        {
                            
                            /*
                             * test internal subroutine:
                             * * circular/non-circular mode
                             */
                            convc1dx(&ca, m, &cb, n, circkind!=0, rkind, 0, &cr1, _state);
                        }
                        else
                        {
                            
                            /*
                             * test internal subroutine - circular mode only
                             */
                            ae_assert(circkind==0, "Convolution test: internal error!", _state);
                            convc1dx(&cb, n, &ca, m, ae_false, rkind, 0, &cr1, _state);
                        }
                    }
                    if( circkind==0 )
                    {
                        testconvunit_refconvc1d(&ca, m, &cb, n, &cr2, _state);
                    }
                    else
                    {
                        testconvunit_refconvc1dcircular(&ca, m, &cb, n, &cr2, _state);
                    }
                    if( circkind==0 )
                    {
                        for(i=0; i<=m+n-2; i++)
                        {
                            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
                        }
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
                        }
                    }
                    
                    /*
                     * Real convolution
                     */
                    ae_vector_set_length(&ra, m, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        ra.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&rb, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        rb.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    ae_vector_set_length(&rr1, 1, _state);
                    if( rkind==-3 )
                    {
                        
                        /*
                         * test wrapper subroutine:
                         * * circular/non-circular
                         */
                        if( circkind==0 )
                        {
                            convr1d(&ra, m, &rb, n, &rr1, _state);
                        }
                        else
                        {
                            convr1dcircular(&ra, m, &rb, n, &rr1, _state);
                        }
                    }
                    else
                    {
                        if( m>=n )
                        {
                            
                            /*
                             * test internal subroutine:
                             * * circular/non-circular mode
                             */
                            convr1dx(&ra, m, &rb, n, circkind!=0, rkind, 0, &rr1, _state);
                        }
                        else
                        {
                            
                            /*
                             * test internal subroutine - non-circular mode only
                             */
                            convr1dx(&rb, n, &ra, m, circkind!=0, rkind, 0, &rr1, _state);
                        }
                    }
                    if( circkind==0 )
                    {
                        testconvunit_refconvr1d(&ra, m, &rb, n, &rr2, _state);
                    }
                    else
                    {
                        testconvunit_refconvr1dcircular(&ra, m, &rb, n, &rr2, _state);
                    }
                    if( circkind==0 )
                    {
                        for(i=0; i<=m+n-2; i++)
                        {
                            refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
                        }
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
                        }
                    }
                }
            }
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    refrerrors = refrerrors||ae_fp_greater(refrerr,errtol);
    
    /*
     * Test inverse convolution
     */
    inverr = 0;
    invrerr = 0;
    for(m=1; m<=maxn; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Complex circilar and non-circular
             */
            ae_vector_set_length(&ca, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ca.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                ca.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                cb.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                cb.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cr1, 1, _state);
            ae_vector_set_length(&cr2, 1, _state);
            convc1d(&ca, m, &cb, n, &cr2, _state);
            convc1dinv(&cr2, m+n-1, &cb, n, &cr1, _state);
            for(i=0; i<=m-1; i++)
            {
                inverr = ae_maxreal(inverr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],ca.ptr.p_complex[i]), _state), _state);
            }
            ae_vector_set_length(&cr1, 1, _state);
            ae_vector_set_length(&cr2, 1, _state);
            convc1dcircular(&ca, m, &cb, n, &cr2, _state);
            convc1dcircularinv(&cr2, m, &cb, n, &cr1, _state);
            for(i=0; i<=m-1; i++)
            {
                inverr = ae_maxreal(inverr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],ca.ptr.p_complex[i]), _state), _state);
            }
            
            /*
             * Real circilar and non-circular
             */
            ae_vector_set_length(&ra, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ra.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                rb.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rr1, 1, _state);
            ae_vector_set_length(&rr2, 1, _state);
            convr1d(&ra, m, &rb, n, &rr2, _state);
            convr1dinv(&rr2, m+n-1, &rb, n, &rr1, _state);
            for(i=0; i<=m-1; i++)
            {
                invrerr = ae_maxreal(invrerr, ae_fabs(rr1.ptr.p_double[i]-ra.ptr.p_double[i], _state), _state);
            }
            ae_vector_set_length(&rr1, 1, _state);
            ae_vector_set_length(&rr2, 1, _state);
            convr1dcircular(&ra, m, &rb, n, &rr2, _state);
            convr1dcircularinv(&rr2, m, &rb, n, &rr1, _state);
            for(i=0; i<=m-1; i++)
            {
                invrerr = ae_maxreal(invrerr, ae_fabs(rr1.ptr.p_double[i]-ra.ptr.p_double[i], _state), _state);
            }
        }
    }
    inverrors = inverrors||ae_fp_greater(inverr,errtol);
    invrerrors = invrerrors||ae_fp_greater(invrerr,errtol);
    
    /*
     * end
     */
    waserrors = ((referrors||refrerrors)||inverrors)||invrerrors;
    if( !silent )
    {
        printf("TESTING CONVOLUTION\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE COMPLEX CONV:         ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE REAL CONV:            ");
        if( refrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* COMPLEX INVERSE:                        ");
        if( inverrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* REAL INVERSE:                           ");
        if( invrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testconvunit_refconvc1d(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    ae_complex v;

    ae_vector_clear(r);

    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=m+n-2; i++)
    {
        r->ptr.p_complex[i] = ae_complex_from_d(0);
    }
    for(i=0; i<=m-1; i++)
    {
        v = a->ptr.p_complex[i];
        ae_v_caddc(&r->ptr.p_complex[i], 1, &b->ptr.p_complex[0], 1, "N", ae_v_len(i,i+n-1), v);
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testconvunit_refconvc1dcircular(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j2;
    ae_vector buf;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(r);
    ae_vector_init(&buf, 0, DT_COMPLEX, _state, ae_true);

    testconvunit_refconvc1d(a, m, b, n, &buf, _state);
    ae_vector_set_length(r, m, _state);
    ae_v_cmove(&r->ptr.p_complex[0], 1, &buf.ptr.p_complex[0], 1, "N", ae_v_len(0,m-1));
    i1 = m;
    while(i1<=m+n-2)
    {
        i2 = ae_minint(i1+m-1, m+n-2, _state);
        j2 = i2-i1;
        ae_v_cadd(&r->ptr.p_complex[0], 1, &buf.ptr.p_complex[i1], 1, "N", ae_v_len(0,j2));
        i1 = i1+m;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference FFT
*************************************************************************/
static void testconvunit_refconvr1d(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    double v;

    ae_vector_clear(r);

    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=m+n-2; i++)
    {
        r->ptr.p_double[i] = 0;
    }
    for(i=0; i<=m-1; i++)
    {
        v = a->ptr.p_double[i];
        ae_v_addd(&r->ptr.p_double[i], 1, &b->ptr.p_double[0], 1, ae_v_len(i,i+n-1), v);
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testconvunit_refconvr1dcircular(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j2;
    ae_vector buf;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(r);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    testconvunit_refconvr1d(a, m, b, n, &buf, _state);
    ae_vector_set_length(r, m, _state);
    ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m-1));
    i1 = m;
    while(i1<=m+n-2)
    {
        i2 = ae_minint(i1+m-1, m+n-2, _state);
        j2 = i2-i1;
        ae_v_add(&r->ptr.p_double[0], 1, &buf.ptr.p_double[i1], 1, ae_v_len(0,j2));
        i1 = i1+m;
    }
    ae_frame_leave(_state);
}



static void testcorrunit_refcorrc1d(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refcorrc1dcircular(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refcorrr1d(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refcorrr1dcircular(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refconvc1d(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refconvc1dcircular(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refconvr1d(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state);
static void testcorrunit_refconvr1dcircular(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testcorr(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t m;
    ae_int_t n;
    ae_int_t i;
    ae_vector ra;
    ae_vector rb;
    ae_vector rr1;
    ae_vector rr2;
    ae_vector ca;
    ae_vector cb;
    ae_vector cr1;
    ae_vector cr2;
    ae_int_t maxn;
    double referr;
    double refrerr;
    double errtol;
    ae_bool referrors;
    ae_bool refrerrors;
    ae_bool inverrors;
    ae_bool invrerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&ra, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rb, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&rr2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ca, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cb, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr1, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&cr2, 0, DT_COMPLEX, _state, ae_true);

    maxn = 32;
    errtol = 100000*ae_pow(maxn, (double)3/(double)2, _state)*ae_machineepsilon;
    referrors = ae_false;
    refrerrors = ae_false;
    inverrors = ae_false;
    invrerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test against reference O(N^2) implementation.
     */
    referr = 0;
    refrerr = 0;
    for(m=1; m<=maxn; m++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * Complex correlation
             */
            ae_vector_set_length(&ca, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ca.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                ca.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                cb.ptr.p_complex[i].x = 2*ae_randomreal(_state)-1;
                cb.ptr.p_complex[i].y = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&cr1, 1, _state);
            corrc1d(&ca, m, &cb, n, &cr1, _state);
            testcorrunit_refcorrc1d(&ca, m, &cb, n, &cr2, _state);
            for(i=0; i<=m+n-2; i++)
            {
                referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
            }
            ae_vector_set_length(&cr1, 1, _state);
            corrc1dcircular(&ca, m, &cb, n, &cr1, _state);
            testcorrunit_refcorrc1dcircular(&ca, m, &cb, n, &cr2, _state);
            for(i=0; i<=m-1; i++)
            {
                referr = ae_maxreal(referr, ae_c_abs(ae_c_sub(cr1.ptr.p_complex[i],cr2.ptr.p_complex[i]), _state), _state);
            }
            
            /*
             * Real correlation
             */
            ae_vector_set_length(&ra, m, _state);
            for(i=0; i<=m-1; i++)
            {
                ra.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rb, n, _state);
            for(i=0; i<=n-1; i++)
            {
                rb.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            ae_vector_set_length(&rr1, 1, _state);
            corrr1d(&ra, m, &rb, n, &rr1, _state);
            testcorrunit_refcorrr1d(&ra, m, &rb, n, &rr2, _state);
            for(i=0; i<=m+n-2; i++)
            {
                refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
            }
            ae_vector_set_length(&rr1, 1, _state);
            corrr1dcircular(&ra, m, &rb, n, &rr1, _state);
            testcorrunit_refcorrr1dcircular(&ra, m, &rb, n, &rr2, _state);
            for(i=0; i<=m-1; i++)
            {
                refrerr = ae_maxreal(refrerr, ae_fabs(rr1.ptr.p_double[i]-rr2.ptr.p_double[i], _state), _state);
            }
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    refrerrors = refrerrors||ae_fp_greater(refrerr,errtol);
    
    /*
     * end
     */
    waserrors = referrors||refrerrors;
    if( !silent )
    {
        printf("TESTING CORRELATION\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE COMPLEX CORR:         ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE REAL CORR:            ");
        if( refrerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrc1d(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_complex v;
    ae_vector s;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(r);
    ae_vector_init(&s, 0, DT_COMPLEX, _state, ae_true);

    ae_vector_set_length(&s, m+n-1, _state);
    ae_v_cmove(&s.ptr.p_complex[0], 1, &signal->ptr.p_complex[0], 1, "N", ae_v_len(0,n-1));
    for(i=n; i<=m+n-2; i++)
    {
        s.ptr.p_complex[i] = ae_complex_from_d(0);
    }
    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=n-1; i++)
    {
        v = ae_complex_from_d(0);
        for(j=0; j<=m-1; j++)
        {
            if( i+j>=n )
            {
                break;
            }
            v = ae_c_add(v,ae_c_mul(ae_c_conj(pattern->ptr.p_complex[j], _state),s.ptr.p_complex[i+j]));
        }
        r->ptr.p_complex[i] = v;
    }
    for(i=1; i<=m-1; i++)
    {
        v = ae_complex_from_d(0);
        for(j=i; j<=m-1; j++)
        {
            v = ae_c_add(v,ae_c_mul(ae_c_conj(pattern->ptr.p_complex[j], _state),s.ptr.p_complex[j-i]));
        }
        r->ptr.p_complex[m+n-1-i] = v;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrc1dcircular(/* Complex */ ae_vector* signal,
     ae_int_t n,
     /* Complex */ ae_vector* pattern,
     ae_int_t m,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_complex v;

    ae_vector_clear(r);

    ae_vector_set_length(r, n, _state);
    for(i=0; i<=n-1; i++)
    {
        v = ae_complex_from_d(0);
        for(j=0; j<=m-1; j++)
        {
            v = ae_c_add(v,ae_c_mul(ae_c_conj(pattern->ptr.p_complex[j], _state),signal->ptr.p_complex[(i+j)%n]));
        }
        r->ptr.p_complex[i] = v;
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrr1d(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    double v;
    ae_vector s;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(r);
    ae_vector_init(&s, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&s, m+n-1, _state);
    ae_v_move(&s.ptr.p_double[0], 1, &signal->ptr.p_double[0], 1, ae_v_len(0,n-1));
    for(i=n; i<=m+n-2; i++)
    {
        s.ptr.p_double[i] = 0;
    }
    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=n-1; i++)
    {
        v = 0;
        for(j=0; j<=m-1; j++)
        {
            if( i+j>=n )
            {
                break;
            }
            v = v+pattern->ptr.p_double[j]*s.ptr.p_double[i+j];
        }
        r->ptr.p_double[i] = v;
    }
    for(i=1; i<=m-1; i++)
    {
        v = 0;
        for(j=i; j<=m-1; j++)
        {
            v = v+pattern->ptr.p_double[j]*s.ptr.p_double[-i+j];
        }
        r->ptr.p_double[m+n-1-i] = v;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refcorrr1dcircular(/* Real    */ ae_vector* signal,
     ae_int_t n,
     /* Real    */ ae_vector* pattern,
     ae_int_t m,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;

    ae_vector_clear(r);

    ae_vector_set_length(r, n, _state);
    for(i=0; i<=n-1; i++)
    {
        v = 0;
        for(j=0; j<=m-1; j++)
        {
            v = v+pattern->ptr.p_double[j]*signal->ptr.p_double[(i+j)%n];
        }
        r->ptr.p_double[i] = v;
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refconvc1d(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    ae_complex v;

    ae_vector_clear(r);

    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=m+n-2; i++)
    {
        r->ptr.p_complex[i] = ae_complex_from_d(0);
    }
    for(i=0; i<=m-1; i++)
    {
        v = a->ptr.p_complex[i];
        ae_v_caddc(&r->ptr.p_complex[i], 1, &b->ptr.p_complex[0], 1, "N", ae_v_len(i,i+n-1), v);
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refconvc1dcircular(/* Complex */ ae_vector* a,
     ae_int_t m,
     /* Complex */ ae_vector* b,
     ae_int_t n,
     /* Complex */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j2;
    ae_vector buf;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(r);
    ae_vector_init(&buf, 0, DT_COMPLEX, _state, ae_true);

    testcorrunit_refconvc1d(a, m, b, n, &buf, _state);
    ae_vector_set_length(r, m, _state);
    ae_v_cmove(&r->ptr.p_complex[0], 1, &buf.ptr.p_complex[0], 1, "N", ae_v_len(0,m-1));
    i1 = m;
    while(i1<=m+n-2)
    {
        i2 = ae_minint(i1+m-1, m+n-2, _state);
        j2 = i2-i1;
        ae_v_cadd(&r->ptr.p_complex[0], 1, &buf.ptr.p_complex[i1], 1, "N", ae_v_len(0,j2));
        i1 = i1+m;
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference FFT
*************************************************************************/
static void testcorrunit_refconvr1d(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_int_t i;
    double v;

    ae_vector_clear(r);

    ae_vector_set_length(r, m+n-1, _state);
    for(i=0; i<=m+n-2; i++)
    {
        r->ptr.p_double[i] = 0;
    }
    for(i=0; i<=m-1; i++)
    {
        v = a->ptr.p_double[i];
        ae_v_addd(&r->ptr.p_double[i], 1, &b->ptr.p_double[0], 1, ae_v_len(i,i+n-1), v);
    }
}


/*************************************************************************
Reference implementation
*************************************************************************/
static void testcorrunit_refconvr1dcircular(/* Real    */ ae_vector* a,
     ae_int_t m,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     /* Real    */ ae_vector* r,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i1;
    ae_int_t i2;
    ae_int_t j2;
    ae_vector buf;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(r);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    testcorrunit_refconvr1d(a, m, b, n, &buf, _state);
    ae_vector_set_length(r, m, _state);
    ae_v_move(&r->ptr.p_double[0], 1, &buf.ptr.p_double[0], 1, ae_v_len(0,m-1));
    i1 = m;
    while(i1<=m+n-2)
    {
        i2 = ae_minint(i1+m-1, m+n-2, _state);
        j2 = i2-i1;
        ae_v_add(&r->ptr.p_double[0], 1, &buf.ptr.p_double[i1], 1, ae_v_len(0,j2));
        i1 = i1+m;
    }
    ae_frame_leave(_state);
}



static void testfhtunit_reffhtr1d(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);
static void testfhtunit_reffhtr1dinv(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testfht(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t n;
    ae_int_t i;
    ae_vector r1;
    ae_vector r2;
    ae_vector r3;
    ae_int_t maxn;
    double bidierr;
    double referr;
    double errtol;
    ae_bool referrors;
    ae_bool bidierrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&r1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r3, 0, DT_REAL, _state, ae_true);

    maxn = 128;
    errtol = 100000*ae_pow(maxn, (double)3/(double)2, _state)*ae_machineepsilon;
    bidierrors = ae_false;
    referrors = ae_false;
    waserrors = ae_false;
    
    /*
     * Test bi-directional error: norm(x-invFHT(FHT(x)))
     */
    bidierr = 0;
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * FHT/invFHT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        ae_vector_set_length(&r3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
            r3.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fhtr1d(&r2, n, _state);
        fhtr1dinv(&r2, n, _state);
        fhtr1dinv(&r3, n, _state);
        fhtr1d(&r3, n, _state);
        for(i=0; i<=n-1; i++)
        {
            bidierr = ae_maxreal(bidierr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
            bidierr = ae_maxreal(bidierr, ae_fabs(r1.ptr.p_double[i]-r3.ptr.p_double[i], _state), _state);
        }
    }
    bidierrors = bidierrors||ae_fp_greater(bidierr,errtol);
    
    /*
     * Test against reference O(N^2) implementation
     */
    referr = 0;
    for(n=1; n<=maxn; n++)
    {
        
        /*
         * FHT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fhtr1d(&r1, n, _state);
        testfhtunit_reffhtr1d(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
        }
        
        /*
         * inverse FHT
         */
        ae_vector_set_length(&r1, n, _state);
        ae_vector_set_length(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            r1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            r2.ptr.p_double[i] = r1.ptr.p_double[i];
        }
        fhtr1dinv(&r1, n, _state);
        testfhtunit_reffhtr1dinv(&r2, n, _state);
        for(i=0; i<=n-1; i++)
        {
            referr = ae_maxreal(referr, ae_fabs(r1.ptr.p_double[i]-r2.ptr.p_double[i], _state), _state);
        }
    }
    referrors = referrors||ae_fp_greater(referr,errtol);
    
    /*
     * end
     */
    waserrors = bidierrors||referrors;
    if( !silent )
    {
        printf("TESTING FHT\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* BI-DIRECTIONAL TEST:                    ");
        if( bidierrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* AGAINST REFERENCE FHT:                  ");
        if( referrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Reference FHT
*************************************************************************/
static void testfhtunit_reffhtr1d(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector buf;
    ae_int_t i;
    ae_int_t j;
    double v;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&buf, 0, DT_REAL, _state, ae_true);

    ae_assert(n>0, "RefFHTR1D: incorrect N!", _state);
    ae_vector_set_length(&buf, n, _state);
    for(i=0; i<=n-1; i++)
    {
        v = 0;
        for(j=0; j<=n-1; j++)
        {
            v = v+a->ptr.p_double[j]*(ae_cos(2*ae_pi*i*j/n, _state)+ae_sin(2*ae_pi*i*j/n, _state));
        }
        buf.ptr.p_double[i] = v;
    }
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_double[i] = buf.ptr.p_double[i];
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Reference inverse FHT
*************************************************************************/
static void testfhtunit_reffhtr1dinv(/* Real    */ ae_vector* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;


    ae_assert(n>0, "RefFHTR1DInv: incorrect N!", _state);
    testfhtunit_reffhtr1d(a, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a->ptr.p_double[i] = a->ptr.p_double[i]/n;
    }
}



static double testgqunit_mapkind(ae_int_t k, ae_state *_state);
static void testgqunit_buildgausslegendrequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);
static void testgqunit_buildgaussjacobiquadrature(ae_int_t n,
     double alpha,
     double beta,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);
static void testgqunit_buildgausslaguerrequadrature(ae_int_t n,
     double alpha,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);
static void testgqunit_buildgausshermitequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testgq(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector alpha;
    ae_vector beta;
    ae_vector x;
    ae_vector w;
    ae_vector x2;
    ae_vector w2;
    double err;
    ae_int_t n;
    ae_int_t i;
    ae_int_t info;
    ae_int_t akind;
    ae_int_t bkind;
    double alphac;
    double betac;
    double errtol;
    double nonstricterrtol;
    double stricterrtol;
    ae_bool recerrors;
    ae_bool specerrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&alpha, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&beta, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);

    recerrors = ae_false;
    specerrors = ae_false;
    waserrors = ae_false;
    errtol = 1.0E-12;
    nonstricterrtol = 1.0E-6;
    stricterrtol = 1000*ae_machineepsilon;
    
    /*
     * Three tests for rec-based Gauss quadratures with known weights/nodes:
     * 1. Gauss-Legendre with N=2
     * 2. Gauss-Legendre with N=5
     * 3. Gauss-Chebyshev with N=1, 2, 4, 8, ..., 512
     */
    err = 0;
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 2, _state);
    alpha.ptr.p_double[0] = 0;
    alpha.ptr.p_double[1] = 0;
    beta.ptr.p_double[1] = (double)1/(double)(4*1*1-1);
    gqgeneraterec(&alpha, &beta, 2.0, 2, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+ae_sqrt(3, _state)/3, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]-ae_sqrt(3, _state)/3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-1, _state), _state);
        for(i=0; i<=0; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 5, _state);
    ae_vector_set_length(&beta, 5, _state);
    alpha.ptr.p_double[0] = 0;
    for(i=1; i<=4; i++)
    {
        alpha.ptr.p_double[i] = 0;
        beta.ptr.p_double[i] = ae_sqr(i, _state)/(4*ae_sqr(i, _state)-1);
    }
    gqgeneraterec(&alpha, &beta, 2.0, 5, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+ae_sqrt(245+14*ae_sqrt(70, _state), _state)/21, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+x.ptr.p_double[4], _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+ae_sqrt(245-14*ae_sqrt(70, _state), _state)/21, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+x.ptr.p_double[3], _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2], _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(322-13*ae_sqrt(70, _state))/900, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-w.ptr.p_double[4], _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(322+13*ae_sqrt(70, _state))/900, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-w.ptr.p_double[3], _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)128/(double)225, _state), _state);
        for(i=0; i<=3; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    n = 1;
    while(n<=512)
    {
        ae_vector_set_length(&alpha, n, _state);
        ae_vector_set_length(&beta, n, _state);
        for(i=0; i<=n-1; i++)
        {
            alpha.ptr.p_double[i] = 0;
            if( i==0 )
            {
                beta.ptr.p_double[i] = 0;
            }
            if( i==1 )
            {
                beta.ptr.p_double[i] = (double)1/(double)2;
            }
            if( i>1 )
            {
                beta.ptr.p_double[i] = (double)1/(double)4;
            }
        }
        gqgeneraterec(&alpha, &beta, ae_pi, n, &info, &x, &w, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-ae_cos(ae_pi*(n-i-0.5)/n, _state), _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-ae_pi/n, _state), _state);
            }
            for(i=0; i<=n-2; i++)
            {
                recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
            }
        }
        else
        {
            recerrors = ae_true;
        }
        n = n*2;
    }
    recerrors = recerrors||ae_fp_greater(err,errtol);
    
    /*
     * Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes:
     * 1. Gauss-Lobatto with N=3
     * 2. Gauss-Lobatto with N=4
     * 3. Gauss-Lobatto with N=6
     */
    err = 0;
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 2, _state);
    alpha.ptr.p_double[0] = 0;
    alpha.ptr.p_double[1] = 0;
    beta.ptr.p_double[0] = 0;
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    gqgenerategausslobattorec(&alpha, &beta, 2.0, -1, 1, 3, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1], _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)1/(double)3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(double)4/(double)3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)1/(double)3, _state), _state);
        for(i=0; i<=1; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 3, _state);
    ae_vector_set_length(&beta, 3, _state);
    alpha.ptr.p_double[0] = 0;
    alpha.ptr.p_double[1] = 0;
    alpha.ptr.p_double[2] = 0;
    beta.ptr.p_double[0] = 0;
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    beta.ptr.p_double[2] = (double)(2*2)/(double)(4*2*2-1);
    gqgenerategausslobattorec(&alpha, &beta, 2.0, -1, 1, 4, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+ae_sqrt(5, _state)/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-ae_sqrt(5, _state)/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[3]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)1/(double)6, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(double)5/(double)6, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)5/(double)6, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[3]-(double)1/(double)6, _state), _state);
        for(i=0; i<=2; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 5, _state);
    ae_vector_set_length(&beta, 5, _state);
    alpha.ptr.p_double[0] = 0;
    alpha.ptr.p_double[1] = 0;
    alpha.ptr.p_double[2] = 0;
    alpha.ptr.p_double[3] = 0;
    alpha.ptr.p_double[4] = 0;
    beta.ptr.p_double[0] = 0;
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    beta.ptr.p_double[2] = (double)(2*2)/(double)(4*2*2-1);
    beta.ptr.p_double[3] = (double)(3*3)/(double)(4*3*3-1);
    beta.ptr.p_double[4] = (double)(4*4)/(double)(4*4*4-1);
    gqgenerategausslobattorec(&alpha, &beta, 2.0, -1, 1, 6, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+ae_sqrt((7+2*ae_sqrt(7, _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]+ae_sqrt((7-2*ae_sqrt(7, _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[3]-ae_sqrt((7-2*ae_sqrt(7, _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[4]-ae_sqrt((7+2*ae_sqrt(7, _state))/21, _state), _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[5]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)1/(double)15, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(14-ae_sqrt(7, _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(14+ae_sqrt(7, _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[3]-(14+ae_sqrt(7, _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[4]-(14-ae_sqrt(7, _state))/30, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[5]-(double)1/(double)15, _state), _state);
        for(i=0; i<=4; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    recerrors = recerrors||ae_fp_greater(err,errtol);
    
    /*
     * Three tests for rec-based Gauss-Radau quadratures with known weights/nodes:
     * 1. Gauss-Radau with N=2
     * 2. Gauss-Radau with N=3
     * 3. Gauss-Radau with N=3 (another case)
     */
    err = 0;
    ae_vector_set_length(&alpha, 1, _state);
    ae_vector_set_length(&beta, 2, _state);
    alpha.ptr.p_double[0] = 0;
    beta.ptr.p_double[0] = 0;
    beta.ptr.p_double[1] = (double)(1*1)/(double)(4*1*1-1);
    gqgenerategaussradaurec(&alpha, &beta, 2.0, -1, 2, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]-(double)1/(double)3, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-0.5, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-1.5, _state), _state);
        for(i=0; i<=0; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 3, _state);
    alpha.ptr.p_double[0] = 0;
    alpha.ptr.p_double[1] = 0;
    for(i=0; i<=2; i++)
    {
        beta.ptr.p_double[i] = ae_sqr(i, _state)/(4*ae_sqr(i, _state)-1);
    }
    gqgenerategaussradaurec(&alpha, &beta, 2.0, -1, 3, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]-(1-ae_sqrt(6, _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-(1+ae_sqrt(6, _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(double)2/(double)9, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(16+ae_sqrt(6, _state))/18, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(16-ae_sqrt(6, _state))/18, _state), _state);
        for(i=0; i<=1; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    ae_vector_set_length(&alpha, 2, _state);
    ae_vector_set_length(&beta, 3, _state);
    alpha.ptr.p_double[0] = 0;
    alpha.ptr.p_double[1] = 0;
    for(i=0; i<=2; i++)
    {
        beta.ptr.p_double[i] = ae_sqr(i, _state)/(4*ae_sqr(i, _state)-1);
    }
    gqgenerategaussradaurec(&alpha, &beta, 2.0, 1, 3, &info, &x, &w, _state);
    if( info>0 )
    {
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[2]-1, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[1]+(1-ae_sqrt(6, _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[0]+(1+ae_sqrt(6, _state))/5, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[2]-(double)2/(double)9, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[1]-(16+ae_sqrt(6, _state))/18, _state), _state);
        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[0]-(16-ae_sqrt(6, _state))/18, _state), _state);
        for(i=0; i<=1; i++)
        {
            recerrors = recerrors||ae_fp_greater_eq(x.ptr.p_double[i],x.ptr.p_double[i+1]);
        }
    }
    else
    {
        recerrors = ae_true;
    }
    recerrors = recerrors||ae_fp_greater(err,errtol);
    
    /*
     * test recurrence-based special cases (Legendre, Jacobi, Hermite, ...)
     * against another implementation (polynomial root-finder)
     */
    for(n=1; n<=20; n++)
    {
        
        /*
         * test gauss-legendre
         */
        err = 0;
        gqgenerategausslegendre(n, &info, &x, &w, _state);
        if( info>0 )
        {
            testgqunit_buildgausslegendrequadrature(n, &x2, &w2, _state);
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
            }
        }
        else
        {
            specerrors = ae_true;
        }
        specerrors = specerrors||ae_fp_greater(err,errtol);
        
        /*
         * Test Gauss-Jacobi.
         * Since task is much more difficult we will use less strict
         * threshold.
         */
        err = 0;
        for(akind=0; akind<=9; akind++)
        {
            for(bkind=0; bkind<=9; bkind++)
            {
                alphac = testgqunit_mapkind(akind, _state);
                betac = testgqunit_mapkind(bkind, _state);
                gqgenerategaussjacobi(n, alphac, betac, &info, &x, &w, _state);
                if( info>0 )
                {
                    testgqunit_buildgaussjacobiquadrature(n, alphac, betac, &x2, &w2, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
                    }
                }
                else
                {
                    specerrors = ae_true;
                }
            }
        }
        specerrors = specerrors||ae_fp_greater(err,nonstricterrtol);
        
        /*
         * special test for Gauss-Jacobi (Chebyshev weight
         * function with analytically known nodes/weights)
         */
        err = 0;
        gqgenerategaussjacobi(n, -0.5, -0.5, &info, &x, &w, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]+ae_cos(ae_pi*(i+0.5)/n, _state), _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-ae_pi/n, _state), _state);
            }
        }
        else
        {
            specerrors = ae_true;
        }
        specerrors = specerrors||ae_fp_greater(err,stricterrtol);
        
        /*
         * Test Gauss-Laguerre
         */
        err = 0;
        for(akind=0; akind<=9; akind++)
        {
            alphac = testgqunit_mapkind(akind, _state);
            gqgenerategausslaguerre(n, alphac, &info, &x, &w, _state);
            if( info>0 )
            {
                testgqunit_buildgausslaguerrequadrature(n, alphac, &x2, &w2, _state);
                for(i=0; i<=n-1; i++)
                {
                    err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                    err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
                }
            }
            else
            {
                specerrors = ae_true;
            }
        }
        specerrors = specerrors||ae_fp_greater(err,nonstricterrtol);
        
        /*
         * Test Gauss-Hermite
         */
        err = 0;
        gqgenerategausshermite(n, &info, &x, &w, _state);
        if( info>0 )
        {
            testgqunit_buildgausshermitequadrature(n, &x2, &w2, _state);
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x.ptr.p_double[i]-x2.ptr.p_double[i], _state), _state);
                err = ae_maxreal(err, ae_fabs(w.ptr.p_double[i]-w2.ptr.p_double[i], _state), _state);
            }
        }
        else
        {
            specerrors = ae_true;
        }
        specerrors = specerrors||ae_fp_greater(err,nonstricterrtol);
    }
    
    /*
     * end
     */
    waserrors = recerrors||specerrors;
    if( !silent )
    {
        printf("TESTING GAUSS QUADRATURES\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SPECIAL CASES (LEGENDRE/JACOBI/..)      ");
        if( specerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* RECURRENCE-BASED:                       ");
        if( recerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Maps:
    0   =>  -0.9
    1   =>  -0.5
    2   =>  -0.1
    3   =>   0.0
    4   =>  +0.1
    5   =>  +0.5
    6   =>  +0.9
    7   =>  +1.0
    8   =>  +1.5
    9   =>  +2.0
*************************************************************************/
static double testgqunit_mapkind(ae_int_t k, ae_state *_state)
{
    double result;


    result = 0;
    if( k==0 )
    {
        result = -0.9;
    }
    if( k==1 )
    {
        result = -0.5;
    }
    if( k==2 )
    {
        result = -0.1;
    }
    if( k==3 )
    {
        result = 0.0;
    }
    if( k==4 )
    {
        result = 0.1;
    }
    if( k==5 )
    {
        result = 0.5;
    }
    if( k==6 )
    {
        result = 0.9;
    }
    if( k==7 )
    {
        result = 1.0;
    }
    if( k==8 )
    {
        result = 1.5;
    }
    if( k==9 )
    {
        result = 2.0;
    }
    return result;
}


/*************************************************************************
Gauss-Legendre, another variant
*************************************************************************/
static void testgqunit_buildgausslegendrequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double p1;
    double p2;
    double p3;
    double dp3;
    double tmp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    for(i=0; i<=(n+1)/2-1; i++)
    {
        r = ae_cos(ae_pi*(4*i+3)/(4*n+2), _state);
        do
        {
            p2 = 0;
            p3 = 1;
            for(j=0; j<=n-1; j++)
            {
                p1 = p2;
                p2 = p3;
                p3 = ((2*j+1)*r*p2-j*p1)/(j+1);
            }
            dp3 = n*(r*p3-p2)/(r*r-1);
            r1 = r;
            r = r-p3/dp3;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        x->ptr.p_double[n-1-i] = -r;
        w->ptr.p_double[i] = 2/((1-r*r)*dp3*dp3);
        w->ptr.p_double[n-1-i] = 2/((1-r*r)*dp3*dp3);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}


/*************************************************************************
Gauss-Jacobi, another variant
*************************************************************************/
static void testgqunit_buildgaussjacobiquadrature(ae_int_t n,
     double alpha,
     double beta,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double t1;
    double t2;
    double t3;
    double p1;
    double p2;
    double p3;
    double pp;
    double an;
    double bn;
    double a;
    double b;
    double c;
    double tmpsgn;
    double tmp;
    double alfbet;
    double temp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( i==0 )
        {
            an = alpha/n;
            bn = beta/n;
            t1 = (1+alpha)*(2.78/(4+n*n)+0.768*an/n);
            t2 = 1+1.48*an+0.96*bn+0.452*an*an+0.83*an*bn;
            r = (t2-t1)/t2;
        }
        else
        {
            if( i==1 )
            {
                t1 = (4.1+alpha)/((1+alpha)*(1+0.156*alpha));
                t2 = 1+0.06*(n-8)*(1+0.12*alpha)/n;
                t3 = 1+0.012*beta*(1+0.25*ae_fabs(alpha, _state))/n;
                r = r-t1*t2*t3*(1-r);
            }
            else
            {
                if( i==2 )
                {
                    t1 = (1.67+0.28*alpha)/(1+0.37*alpha);
                    t2 = 1+0.22*(n-8)/n;
                    t3 = 1+8*beta/((6.28+beta)*n*n);
                    r = r-t1*t2*t3*(x->ptr.p_double[0]-r);
                }
                else
                {
                    if( i<n-2 )
                    {
                        r = 3*x->ptr.p_double[i-1]-3*x->ptr.p_double[i-2]+x->ptr.p_double[i-3];
                    }
                    else
                    {
                        if( i==n-2 )
                        {
                            t1 = (1+0.235*beta)/(0.766+0.119*beta);
                            t2 = 1/(1+0.639*(n-4)/(1+0.71*(n-4)));
                            t3 = 1/(1+20*alpha/((7.5+alpha)*n*n));
                            r = r+t1*t2*t3*(r-x->ptr.p_double[i-2]);
                        }
                        else
                        {
                            if( i==n-1 )
                            {
                                t1 = (1+0.37*beta)/(1.67+0.28*beta);
                                t2 = 1/(1+0.22*(n-8)/n);
                                t3 = 1/(1+8*alpha/((6.28+alpha)*n*n));
                                r = r+t1*t2*t3*(r-x->ptr.p_double[i-2]);
                            }
                        }
                    }
                }
            }
        }
        alfbet = alpha+beta;
        do
        {
            temp = 2+alfbet;
            p1 = (alpha-beta+temp*r)*0.5;
            p2 = 1;
            for(j=2; j<=n; j++)
            {
                p3 = p2;
                p2 = p1;
                temp = 2*j+alfbet;
                a = 2*j*(j+alfbet)*(temp-2);
                b = (temp-1)*(alpha*alpha-beta*beta+temp*(temp-2)*r);
                c = 2*(j-1+alpha)*(j-1+beta)*temp;
                p1 = (b*p2-c*p3)/a;
            }
            pp = (n*(alpha-beta-temp*r)*p1+2*(n+alpha)*(n+beta)*p2)/(temp*(1-r*r));
            r1 = r;
            r = r1-p1/pp;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        w->ptr.p_double[i] = ae_exp(lngamma(alpha+n, &tmpsgn, _state)+lngamma(beta+n, &tmpsgn, _state)-lngamma(n+1, &tmpsgn, _state)-lngamma(n+alfbet+1, &tmpsgn, _state), _state)*temp*ae_pow(2, alfbet, _state)/(pp*p2);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}


/*************************************************************************
Gauss-Laguerre, another variant
*************************************************************************/
static void testgqunit_buildgausslaguerrequadrature(ae_int_t n,
     double alpha,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double p1;
    double p2;
    double p3;
    double dp3;
    double tsg;
    double tmp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        if( i==0 )
        {
            r = (1+alpha)*(3+0.92*alpha)/(1+2.4*n+1.8*alpha);
        }
        else
        {
            if( i==1 )
            {
                r = r+(15+6.25*alpha)/(1+0.9*alpha+2.5*n);
            }
            else
            {
                r = r+((1+2.55*(i-1))/(1.9*(i-1))+1.26*(i-1)*alpha/(1+3.5*(i-1)))/(1+0.3*alpha)*(r-x->ptr.p_double[i-2]);
            }
        }
        do
        {
            p2 = 0;
            p3 = 1;
            for(j=0; j<=n-1; j++)
            {
                p1 = p2;
                p2 = p3;
                p3 = ((-r+2*j+alpha+1)*p2-(j+alpha)*p1)/(j+1);
            }
            dp3 = (n*p3-(n+alpha)*p2)/r;
            r1 = r;
            r = r-p3/dp3;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        w->ptr.p_double[i] = -ae_exp(lngamma(alpha+n, &tsg, _state)-lngamma(n, &tsg, _state), _state)/(dp3*n*p2);
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}


/*************************************************************************
Gauss-Hermite, another variant
*************************************************************************/
static void testgqunit_buildgausshermitequadrature(ae_int_t n,
     /* Real    */ ae_vector* x,
     /* Real    */ ae_vector* w,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double r;
    double r1;
    double p1;
    double p2;
    double p3;
    double dp3;
    double pipm4;
    double tmp;

    ae_vector_clear(x);
    ae_vector_clear(w);

    ae_vector_set_length(x, n-1+1, _state);
    ae_vector_set_length(w, n-1+1, _state);
    pipm4 = ae_pow(ae_pi, -0.25, _state);
    for(i=0; i<=(n+1)/2-1; i++)
    {
        if( i==0 )
        {
            r = ae_sqrt(2*n+1, _state)-1.85575*ae_pow(2*n+1, -(double)1/(double)6, _state);
        }
        else
        {
            if( i==1 )
            {
                r = r-1.14*ae_pow(n, 0.426, _state)/r;
            }
            else
            {
                if( i==2 )
                {
                    r = 1.86*r-0.86*x->ptr.p_double[0];
                }
                else
                {
                    if( i==3 )
                    {
                        r = 1.91*r-0.91*x->ptr.p_double[1];
                    }
                    else
                    {
                        r = 2*r-x->ptr.p_double[i-2];
                    }
                }
            }
        }
        do
        {
            p2 = 0;
            p3 = pipm4;
            for(j=0; j<=n-1; j++)
            {
                p1 = p2;
                p2 = p3;
                p3 = p2*r*ae_sqrt((double)2/(double)(j+1), _state)-p1*ae_sqrt((double)j/(double)(j+1), _state);
            }
            dp3 = ae_sqrt(2*j, _state)*p2;
            r1 = r;
            r = r-p3/dp3;
        }
        while(ae_fp_greater_eq(ae_fabs(r-r1, _state),ae_machineepsilon*(1+ae_fabs(r, _state))*100));
        x->ptr.p_double[i] = r;
        w->ptr.p_double[i] = 2/(dp3*dp3);
        x->ptr.p_double[n-1-i] = -x->ptr.p_double[i];
        w->ptr.p_double[n-1-i] = w->ptr.p_double[i];
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-2-i; j++)
        {
            if( ae_fp_greater_eq(x->ptr.p_double[j],x->ptr.p_double[j+1]) )
            {
                tmp = x->ptr.p_double[j];
                x->ptr.p_double[j] = x->ptr.p_double[j+1];
                x->ptr.p_double[j+1] = tmp;
                tmp = w->ptr.p_double[j];
                w->ptr.p_double[j] = w->ptr.p_double[j+1];
                w->ptr.p_double[j+1] = tmp;
            }
        }
    }
}



static double testgkqunit_mapkind(ae_int_t k, ae_state *_state);





/*************************************************************************
Test
*************************************************************************/
ae_bool testgkq(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pkind;
    double errtol;
    double eps;
    double nonstricterrtol;
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    ae_int_t info;
    double err;
    ae_int_t akind;
    ae_int_t bkind;
    double alphac;
    double betac;
    ae_vector x1;
    ae_vector wg1;
    ae_vector wk1;
    ae_vector x2;
    ae_vector wg2;
    ae_vector wk2;
    ae_int_t info1;
    ae_int_t info2;
    ae_bool successatleastonce;
    ae_bool intblerrors;
    ae_bool vstblerrors;
    ae_bool generrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wg1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wk1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wg2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&wk2, 0, DT_REAL, _state, ae_true);

    intblerrors = ae_false;
    vstblerrors = ae_false;
    generrors = ae_false;
    waserrors = ae_false;
    errtol = 10000*ae_machineepsilon;
    nonstricterrtol = 1000*errtol;
    
    /*
     * test recurrence-based Legendre nodes against the precalculated table
     */
    for(pkind=0; pkind<=5; pkind++)
    {
        n = 0;
        if( pkind==0 )
        {
            n = 15;
        }
        if( pkind==1 )
        {
            n = 21;
        }
        if( pkind==2 )
        {
            n = 31;
        }
        if( pkind==3 )
        {
            n = 41;
        }
        if( pkind==4 )
        {
            n = 51;
        }
        if( pkind==5 )
        {
            n = 61;
        }
        gkqlegendrecalc(n, &info, &x1, &wk1, &wg1, _state);
        gkqlegendretbl(n, &x2, &wk2, &wg2, &eps, _state);
        if( info<=0 )
        {
            generrors = ae_true;
            break;
        }
        for(i=0; i<=n-1; i++)
        {
            vstblerrors = vstblerrors||ae_fp_greater(ae_fabs(x1.ptr.p_double[i]-x2.ptr.p_double[i], _state),errtol);
            vstblerrors = vstblerrors||ae_fp_greater(ae_fabs(wk1.ptr.p_double[i]-wk2.ptr.p_double[i], _state),errtol);
            vstblerrors = vstblerrors||ae_fp_greater(ae_fabs(wg1.ptr.p_double[i]-wg2.ptr.p_double[i], _state),errtol);
        }
    }
    
    /*
     * Test recurrence-baced Gauss-Kronrod nodes against Gauss-only nodes
     * calculated with subroutines from GQ unit.
     */
    for(k=1; k<=30; k++)
    {
        n = 2*k+1;
        
        /*
         * Gauss-Legendre
         */
        err = 0;
        gkqgenerategausslegendre(n, &info1, &x1, &wk1, &wg1, _state);
        gqgenerategausslegendre(k, &info2, &x2, &wg2, _state);
        if( info1>0&&info2>0 )
        {
            for(i=0; i<=k-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(x1.ptr.p_double[2*i+1]-x2.ptr.p_double[i], _state), _state);
                err = ae_maxreal(err, ae_fabs(wg1.ptr.p_double[2*i+1]-wg2.ptr.p_double[i], _state), _state);
            }
        }
        else
        {
            generrors = ae_true;
        }
        generrors = generrors||ae_fp_greater(err,errtol);
    }
    for(k=1; k<=15; k++)
    {
        n = 2*k+1;
        
        /*
         * Gauss-Jacobi
         */
        successatleastonce = ae_false;
        err = 0;
        for(akind=0; akind<=9; akind++)
        {
            for(bkind=0; bkind<=9; bkind++)
            {
                alphac = testgkqunit_mapkind(akind, _state);
                betac = testgkqunit_mapkind(bkind, _state);
                gkqgenerategaussjacobi(n, alphac, betac, &info1, &x1, &wk1, &wg1, _state);
                gqgenerategaussjacobi(k, alphac, betac, &info2, &x2, &wg2, _state);
                if( info1>0&&info2>0 )
                {
                    successatleastonce = ae_true;
                    for(i=0; i<=k-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(x1.ptr.p_double[2*i+1]-x2.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(wg1.ptr.p_double[2*i+1]-wg2.ptr.p_double[i], _state), _state);
                    }
                }
                else
                {
                    generrors = generrors||info1!=-5;
                }
            }
        }
        generrors = (generrors||ae_fp_greater(err,errtol))||!successatleastonce;
    }
    
    /*
     * end
     */
    waserrors = (intblerrors||vstblerrors)||generrors;
    if( !silent )
    {
        printf("TESTING GAUSS-KRONROD QUADRATURES\n");
        printf("FINAL RESULT:                             ");
        if( waserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* PRE-CALCULATED TABLE:                   ");
        if( intblerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* CALCULATED AGAINST THE TABLE:           ");
        if( vstblerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* GENERAL PROPERTIES:                     ");
        if( generrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Maps:
    0   =>  -0.9
    1   =>  -0.5
    2   =>  -0.1
    3   =>   0.0
    4   =>  +0.1
    5   =>  +0.5
    6   =>  +0.9
    7   =>  +1.0
    8   =>  +1.5
    9   =>  +2.0
*************************************************************************/
static double testgkqunit_mapkind(ae_int_t k, ae_state *_state)
{
    double result;


    result = 0;
    if( k==0 )
    {
        result = -0.9;
    }
    if( k==1 )
    {
        result = -0.5;
    }
    if( k==2 )
    {
        result = -0.1;
    }
    if( k==3 )
    {
        result = 0.0;
    }
    if( k==4 )
    {
        result = 0.1;
    }
    if( k==5 )
    {
        result = 0.5;
    }
    if( k==6 )
    {
        result = 0.9;
    }
    if( k==7 )
    {
        result = 1.0;
    }
    if( k==8 )
    {
        result = 1.5;
    }
    if( k==9 )
    {
        result = 2.0;
    }
    return result;
}








/*************************************************************************
Test
*************************************************************************/
ae_bool testautogk(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double a;
    double b;
    autogkstate state;
    autogkreport rep;
    double v;
    double exact;
    double eabs;
    double alpha;
    ae_int_t pkind;
    double errtol;
    ae_bool simpleerrors;
    ae_bool sngenderrors;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    _autogkstate_init(&state, _state, ae_true);
    _autogkreport_init(&rep, _state, ae_true);

    simpleerrors = ae_false;
    sngenderrors = ae_false;
    waserrors = ae_false;
    errtol = 10000*ae_machineepsilon;
    
    /*
     * Simple test: integral(exp(x),+-1,+-2), no maximum width requirements
     */
    a = (2*ae_randominteger(2, _state)-1)*1.0;
    b = (2*ae_randominteger(2, _state)-1)*2.0;
    autogksmooth(a, b, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_exp(state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = ae_exp(b, _state)-ae_exp(a, _state);
    eabs = ae_fabs(ae_exp(b, _state)-ae_exp(a, _state), _state);
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * Simple test: integral(exp(x),+-1,+-2), XWidth=0.1
     */
    a = (2*ae_randominteger(2, _state)-1)*1.0;
    b = (2*ae_randominteger(2, _state)-1)*2.0;
    autogksmoothw(a, b, 0.1, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_exp(state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = ae_exp(b, _state)-ae_exp(a, _state);
    eabs = ae_fabs(ae_exp(b, _state)-ae_exp(a, _state), _state);
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * Simple test: integral(cos(100*x),0,2*pi), no maximum width requirements
     */
    a = 0;
    b = 2*ae_pi;
    autogksmooth(a, b, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_cos(100*state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = 0;
    eabs = 4;
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * Simple test: integral(cos(100*x),0,2*pi), XWidth=0.3
     */
    a = 0;
    b = 2*ae_pi;
    autogksmoothw(a, b, 0.3, &state, _state);
    while(autogkiteration(&state, _state))
    {
        state.f = ae_cos(100*state.x, _state);
    }
    autogkresults(&state, &v, &rep, _state);
    exact = 0;
    eabs = 4;
    if( rep.terminationtype<=0 )
    {
        simpleerrors = ae_true;
    }
    else
    {
        simpleerrors = simpleerrors||ae_fp_greater(ae_fabs(exact-v, _state),errtol*eabs);
    }
    
    /*
     * singular problem on [a,b] = [0.1, 0.5]
     *     f2(x) = (1+x)*(b-x)^alpha, -1 < alpha < 1
     */
    for(pkind=0; pkind<=6; pkind++)
    {
        a = 0.1;
        b = 0.5;
        if( pkind==0 )
        {
            alpha = -0.9;
        }
        if( pkind==1 )
        {
            alpha = -0.5;
        }
        if( pkind==2 )
        {
            alpha = -0.1;
        }
        if( pkind==3 )
        {
            alpha = 0.0;
        }
        if( pkind==4 )
        {
            alpha = 0.1;
        }
        if( pkind==5 )
        {
            alpha = 0.5;
        }
        if( pkind==6 )
        {
            alpha = 0.9;
        }
        
        /*
         * f1(x) = (1+x)*(x-a)^alpha, -1 < alpha < 1
         * 1. use singular integrator for [a,b]
         * 2. use singular integrator for [b,a]
         */
        exact = ae_pow(b-a, alpha+2, _state)/(alpha+2)+(1+a)*ae_pow(b-a, alpha+1, _state)/(alpha+1);
        eabs = ae_fabs(exact, _state);
        autogksingular(a, b, alpha, 0.0, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_less(state.xminusa,0.01) )
            {
                state.f = ae_pow(state.xminusa, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(state.x-a, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(v-exact, _state),errtol*eabs);
        }
        autogksingular(b, a, 0.0, alpha, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_greater(state.bminusx,-0.01) )
            {
                state.f = ae_pow(-state.bminusx, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(state.x-a, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(-v-exact, _state),errtol*eabs);
        }
        
        /*
         * f1(x) = (1+x)*(b-x)^alpha, -1 < alpha < 1
         * 1. use singular integrator for [a,b]
         * 2. use singular integrator for [b,a]
         */
        exact = (1+b)*ae_pow(b-a, alpha+1, _state)/(alpha+1)-ae_pow(b-a, alpha+2, _state)/(alpha+2);
        eabs = ae_fabs(exact, _state);
        autogksingular(a, b, 0.0, alpha, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_less(state.bminusx,0.01) )
            {
                state.f = ae_pow(state.bminusx, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(b-state.x, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(v-exact, _state),errtol*eabs);
        }
        autogksingular(b, a, alpha, 0.0, &state, _state);
        while(autogkiteration(&state, _state))
        {
            if( ae_fp_greater(state.xminusa,-0.01) )
            {
                state.f = ae_pow(-state.xminusa, alpha, _state)*(1+state.x);
            }
            else
            {
                state.f = ae_pow(b-state.x, alpha, _state)*(1+state.x);
            }
        }
        autogkresults(&state, &v, &rep, _state);
        if( rep.terminationtype<=0 )
        {
            sngenderrors = ae_true;
        }
        else
        {
            sngenderrors = sngenderrors||ae_fp_greater(ae_fabs(-v-exact, _state),errtol*eabs);
        }
    }
    
    /*
     * end
     */
    waserrors = simpleerrors||sngenderrors;
    if( !silent )
    {
        printf("TESTING AUTOGK\n");
        printf("INTEGRATION WITH GIVEN ACCURACY:          ");
        if( simpleerrors||sngenderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SIMPLE PROBLEMS:                        ");
        if( simpleerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("* SINGULAR PROBLEMS (ENDS OF INTERVAL):   ");
        if( sngenderrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testidwintunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state);
static void testidwintunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state);
static void testidwintunit_testxy(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t nq,
     ae_int_t nw,
     ae_bool* idwerrors,
     ae_state *_state);
static void testidwintunit_testrxy(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     double r,
     ae_bool* idwerrors,
     ae_state *_state);
static void testidwintunit_testdegree(ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t dtask,
     ae_bool* idwerrors,
     ae_state *_state);
static void testidwintunit_testnoisy(ae_bool* idwerrors, ae_state *_state);





/*************************************************************************
Testing IDW interpolation
*************************************************************************/
ae_bool testidwint(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;
    ae_int_t i;
    ae_int_t j;
    double vx;
    double vy;
    double vz;
    ae_int_t d;
    ae_int_t dtask;
    ae_int_t nx;
    ae_int_t nq;
    ae_int_t nw;
    ae_int_t smalln;
    ae_int_t largen;
    ae_bool waserrors;
    ae_bool idwerrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    idwerrors = ae_false;
    smalln = 256;
    largen = 1024;
    nq = 10;
    nw = 18;
    
    /*
     * Simple test:
     * * F = x^3 + sin(pi*y)*z^2 - (x+y)^2
     * * space is either R1=[-1,+1] (other dimensions are
     *   fixed at 0), R1^2 or R1^3.
     ** D = -1, 0, 1, 2
     */
    for(nx=1; nx<=2; nx++)
    {
        ae_matrix_set_length(&xy, largen, nx+1, _state);
        for(i=0; i<=largen-1; i++)
        {
            for(j=0; j<=nx-1; j++)
            {
                xy.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            if( nx>=1 )
            {
                vx = xy.ptr.pp_double[i][0];
            }
            else
            {
                vx = 0;
            }
            if( nx>=2 )
            {
                vy = xy.ptr.pp_double[i][1];
            }
            else
            {
                vy = 0;
            }
            if( nx>=3 )
            {
                vz = xy.ptr.pp_double[i][2];
            }
            else
            {
                vz = 0;
            }
            xy.ptr.pp_double[i][nx] = vx*vx*vx+ae_sin(ae_pi*vy, _state)*ae_sqr(vz, _state)-ae_sqr(vx+vy, _state);
        }
        for(d=-1; d<=2; d++)
        {
            testidwintunit_testxy(&xy, largen, nx, d, nq, nw, &idwerrors, _state);
        }
    }
    
    /*
     * Another simple test:
     * * five points in 2D - (0,0), (0,1), (1,0), (-1,0) (0,-1)
     * * F is random
     * * D = -1, 0, 1, 2
     */
    nx = 2;
    ae_matrix_set_length(&xy, 5, nx+1, _state);
    xy.ptr.pp_double[0][0] = 0;
    xy.ptr.pp_double[0][1] = 0;
    xy.ptr.pp_double[0][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[1][0] = 1;
    xy.ptr.pp_double[1][1] = 0;
    xy.ptr.pp_double[1][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[2][0] = 0;
    xy.ptr.pp_double[2][1] = 1;
    xy.ptr.pp_double[2][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[3][0] = -1;
    xy.ptr.pp_double[3][1] = 0;
    xy.ptr.pp_double[3][2] = 2*ae_randomreal(_state)-1;
    xy.ptr.pp_double[4][0] = 0;
    xy.ptr.pp_double[4][1] = -1;
    xy.ptr.pp_double[4][2] = 2*ae_randomreal(_state)-1;
    for(d=-1; d<=2; d++)
    {
        testidwintunit_testxy(&xy, 5, nx, d, nq, nw, &idwerrors, _state);
    }
    
    /*
     * Degree test.
     *
     * F is either:
     * * constant (DTask=0)
     * * linear (DTask=1)
     * * quadratic (DTask=2)
     *
     * Nodal functions are either
     * * constant (D=0)
     * * linear (D=1)
     * * quadratic (D=2)
     *
     * When DTask<=D, we can interpolate without errors.
     * When DTask>D, we MUST have errors.
     */
    for(nx=1; nx<=3; nx++)
    {
        for(d=0; d<=2; d++)
        {
            for(dtask=0; dtask<=2; dtask++)
            {
                testidwintunit_testdegree(smalln, nx, d, dtask, &idwerrors, _state);
            }
        }
    }
    
    /*
     * Noisy test
     */
    testidwintunit_testnoisy(&idwerrors, _state);
    
    /*
     * report
     */
    waserrors = idwerrors;
    if( !silent )
    {
        printf("TESTING INVERSE DISTANCE WEIGHTING\n");
        printf("* IDW:                                   ");
        if( !idwerrors )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unsets 2D array.
*************************************************************************/
static void testidwintunit_unset2d(/* Complex */ ae_matrix* a,
     ae_state *_state)
{


    ae_matrix_set_length(a, 0+1, 0+1, _state);
    a->ptr.pp_complex[0][0] = ae_complex_from_d(2*ae_randomreal(_state)-1);
}


/*************************************************************************
Unsets 1D array.
*************************************************************************/
static void testidwintunit_unset1d(/* Real    */ ae_vector* a,
     ae_state *_state)
{


    ae_vector_set_length(a, 0+1, _state);
    a->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Testing IDW:
* generate model using N/NX/D/NQ/NW
* test basic properties
*************************************************************************/
static void testidwintunit_testxy(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t nq,
     ae_int_t nw,
     ae_bool* idwerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    double lipschitzstep;
    ae_int_t i;
    ae_int_t i1;
    ae_int_t i2;
    double v;
    double v1;
    double v2;
    double t;
    double l1;
    double l2;
    idwinterpolant z1;
    ae_vector x;

    ae_frame_make(_state, &_frame_block);
    _idwinterpolant_init(&z1, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    threshold = 1000*ae_machineepsilon;
    lipschitzstep = 0.001;
    ae_vector_set_length(&x, nx, _state);
    
    /*
     * build
     */
    idwbuildmodifiedshepard(xy, n, nx, d, nq, nw, &z1, _state);
    
    /*
     * first, test interpolation properties at nodes
     */
    for(i=0; i<=n-1; i++)
    {
        ae_v_move(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        *idwerrors = *idwerrors||ae_fp_neq(idwcalc(&z1, &x, _state),xy->ptr.pp_double[i][nx]);
    }
    
    /*
     * test Lipschitz continuity
     */
    i1 = ae_randominteger(n, _state);
    do
    {
        i2 = ae_randominteger(n, _state);
    }
    while(i2==i1);
    l1 = 0;
    t = 0;
    while(ae_fp_less(t,1))
    {
        v = 1-t;
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v1 = idwcalc(&z1, &x, _state);
        v = 1-(t+lipschitzstep);
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t+lipschitzstep;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v2 = idwcalc(&z1, &x, _state);
        l1 = ae_maxreal(l1, ae_fabs(v2-v1, _state)/lipschitzstep, _state);
        t = t+lipschitzstep;
    }
    l2 = 0;
    t = 0;
    while(ae_fp_less(t,1))
    {
        v = 1-t;
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v1 = idwcalc(&z1, &x, _state);
        v = 1-(t+lipschitzstep/3);
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t+lipschitzstep/3;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v2 = idwcalc(&z1, &x, _state);
        l2 = ae_maxreal(l2, ae_fabs(v2-v1, _state)/(lipschitzstep/3), _state);
        t = t+lipschitzstep/3;
    }
    *idwerrors = *idwerrors||ae_fp_greater(l2,2.0*l1);
    ae_frame_leave(_state);
}


/*************************************************************************
Testing IDW:
* generate model using R-based model
* test basic properties
*************************************************************************/
static void testidwintunit_testrxy(/* Real    */ ae_matrix* xy,
     ae_int_t n,
     ae_int_t nx,
     double r,
     ae_bool* idwerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    double lipschitzstep;
    ae_int_t i;
    ae_int_t i1;
    ae_int_t i2;
    double v;
    double v1;
    double v2;
    double t;
    double l1;
    double l2;
    idwinterpolant z1;
    ae_vector x;

    ae_frame_make(_state, &_frame_block);
    _idwinterpolant_init(&z1, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);

    threshold = 1000*ae_machineepsilon;
    lipschitzstep = 0.001;
    ae_vector_set_length(&x, nx, _state);
    
    /*
     * build
     */
    idwbuildmodifiedshepardr(xy, n, nx, r, &z1, _state);
    
    /*
     * first, test interpolation properties at nodes
     */
    for(i=0; i<=n-1; i++)
    {
        ae_v_move(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
        *idwerrors = *idwerrors||ae_fp_neq(idwcalc(&z1, &x, _state),xy->ptr.pp_double[i][nx]);
    }
    
    /*
     * test Lipschitz continuity
     */
    i1 = ae_randominteger(n, _state);
    do
    {
        i2 = ae_randominteger(n, _state);
    }
    while(i2==i1);
    l1 = 0;
    t = 0;
    while(ae_fp_less(t,1))
    {
        v = 1-t;
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v1 = idwcalc(&z1, &x, _state);
        v = 1-(t+lipschitzstep);
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t+lipschitzstep;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v2 = idwcalc(&z1, &x, _state);
        l1 = ae_maxreal(l1, ae_fabs(v2-v1, _state)/lipschitzstep, _state);
        t = t+lipschitzstep;
    }
    l2 = 0;
    t = 0;
    while(ae_fp_less(t,1))
    {
        v = 1-t;
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v1 = idwcalc(&z1, &x, _state);
        v = 1-(t+lipschitzstep/3);
        ae_v_moved(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i1][0], 1, ae_v_len(0,nx-1), v);
        v = t+lipschitzstep/3;
        ae_v_addd(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i2][0], 1, ae_v_len(0,nx-1), v);
        v2 = idwcalc(&z1, &x, _state);
        l2 = ae_maxreal(l2, ae_fabs(v2-v1, _state)/(lipschitzstep/3), _state);
        t = t+lipschitzstep/3;
    }
    *idwerrors = *idwerrors||ae_fp_greater(l2,2.0*l1);
    ae_frame_leave(_state);
}


/*************************************************************************
Testing degree properties

F is either:
* constant (DTask=0)
* linear (DTask=1)
* quadratic (DTask=2)

Nodal functions are either
* constant (D=0)
* linear (D=1)
* quadratic (D=2)

When DTask<=D, we can interpolate without errors.
When DTask>D, we MUST have errors.
*************************************************************************/
static void testidwintunit_testdegree(ae_int_t n,
     ae_int_t nx,
     ae_int_t d,
     ae_int_t dtask,
     ae_bool* idwerrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_int_t nq;
    ae_int_t nw;
    ae_int_t i;
    ae_int_t j;
    double v;
    double c0;
    ae_vector c1;
    ae_matrix c2;
    ae_vector x;
    ae_matrix xy;
    idwinterpolant z1;
    double v1;
    double v2;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&c1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    _idwinterpolant_init(&z1, _state, ae_true);

    threshold = 1.0E6*ae_machineepsilon;
    nq = 2*(nx*nx+nx+1);
    nw = 10;
    ae_assert(nq<=n, "TestDegree: internal error", _state);
    
    /*
     * prepare model
     */
    c0 = 2*ae_randomreal(_state)-1;
    ae_vector_set_length(&c1, nx, _state);
    for(i=0; i<=nx-1; i++)
    {
        c1.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
    }
    ae_matrix_set_length(&c2, nx, nx, _state);
    for(i=0; i<=nx-1; i++)
    {
        for(j=i+1; j<=nx-1; j++)
        {
            c2.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            c2.ptr.pp_double[j][i] = c2.ptr.pp_double[i][j];
        }
        do
        {
            c2.ptr.pp_double[i][i] = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_fabs(c2.ptr.pp_double[i][i], _state),0.3));
    }
    
    /*
     * prepare points
     */
    ae_matrix_set_length(&xy, n, nx+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=nx-1; j++)
        {
            xy.ptr.pp_double[i][j] = 4*ae_randomreal(_state)-2;
        }
        xy.ptr.pp_double[i][nx] = c0;
        if( dtask>=1 )
        {
            v = ae_v_dotproduct(&c1.ptr.p_double[0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
            xy.ptr.pp_double[i][nx] = xy.ptr.pp_double[i][nx]+v;
        }
        if( dtask==2 )
        {
            for(j=0; j<=nx-1; j++)
            {
                v = ae_v_dotproduct(&c2.ptr.pp_double[j][0], 1, &xy.ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
                xy.ptr.pp_double[i][nx] = xy.ptr.pp_double[i][nx]+xy.ptr.pp_double[i][j]*v;
            }
        }
    }
    
    /*
     * build interpolant, calculate value at random point
     */
    idwbuildmodifiedshepard(&xy, n, nx, d, nq, nw, &z1, _state);
    ae_vector_set_length(&x, nx, _state);
    for(i=0; i<=nx-1; i++)
    {
        x.ptr.p_double[i] = 4*ae_randomreal(_state)-2;
    }
    v1 = idwcalc(&z1, &x, _state);
    
    /*
     * calculate model value at the same point
     */
    v2 = c0;
    if( dtask>=1 )
    {
        v = ae_v_dotproduct(&c1.ptr.p_double[0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nx-1));
        v2 = v2+v;
    }
    if( dtask==2 )
    {
        for(j=0; j<=nx-1; j++)
        {
            v = ae_v_dotproduct(&c2.ptr.pp_double[j][0], 1, &x.ptr.p_double[0], 1, ae_v_len(0,nx-1));
            v2 = v2+x.ptr.p_double[j]*v;
        }
    }
    
    /*
     * Compare
     */
    if( dtask<=d )
    {
        *idwerrors = *idwerrors||ae_fp_greater(ae_fabs(v2-v1, _state),threshold);
    }
    else
    {
        *idwerrors = *idwerrors||ae_fp_less(ae_fabs(v2-v1, _state),threshold);
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Noisy test:
 * F = x^2 + y^2 + z^2 + noise on [-1,+1]^3
 * space is either R1=[-1,+1] (other dimensions are
   fixed at 0), R1^2 or R1^3.
 * D = 1, 2
 * 4096 points is used for function generation,
   4096 points - for testing
 * RMS error of "noisy" model on test set must be
   lower than RMS error of interpolation model.
*************************************************************************/
static void testidwintunit_testnoisy(ae_bool* idwerrors, ae_state *_state)
{
    ae_frame _frame_block;
    double noiselevel;
    ae_int_t nq;
    ae_int_t nw;
    ae_int_t d;
    ae_int_t nx;
    ae_int_t ntrn;
    ae_int_t ntst;
    ae_int_t i;
    ae_int_t j;
    double v;
    double t;
    double v1;
    double v2;
    double ve;
    ae_matrix xy;
    ae_vector x;
    idwinterpolant z1;
    idwinterpolant z2;
    double rms1;
    double rms2;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    _idwinterpolant_init(&z1, _state, ae_true);
    _idwinterpolant_init(&z2, _state, ae_true);

    nq = 20;
    nw = 40;
    noiselevel = 0.2;
    ntrn = 256;
    ntst = 1024;
    for(d=1; d<=2; d++)
    {
        for(nx=1; nx<=2; nx++)
        {
            
            /*
             * prepare dataset
             */
            ae_matrix_set_length(&xy, ntrn, nx+1, _state);
            for(i=0; i<=ntrn-1; i++)
            {
                v = noiselevel*(2*ae_randomreal(_state)-1);
                for(j=0; j<=nx-1; j++)
                {
                    t = 2*ae_randomreal(_state)-1;
                    v = v+ae_sqr(t, _state);
                    xy.ptr.pp_double[i][j] = t;
                }
                xy.ptr.pp_double[i][nx] = v;
            }
            
            /*
             * build interpolants
             */
            idwbuildmodifiedshepard(&xy, ntrn, nx, d, nq, nw, &z1, _state);
            idwbuildnoisy(&xy, ntrn, nx, d, nq, nw, &z2, _state);
            
            /*
             * calculate RMS errors
             */
            ae_vector_set_length(&x, nx, _state);
            rms1 = 0;
            rms2 = 0;
            for(i=0; i<=ntst-1; i++)
            {
                ve = 0;
                for(j=0; j<=nx-1; j++)
                {
                    t = 2*ae_randomreal(_state)-1;
                    x.ptr.p_double[j] = t;
                    ve = ve+ae_sqr(t, _state);
                }
                v1 = idwcalc(&z1, &x, _state);
                v2 = idwcalc(&z2, &x, _state);
                rms1 = rms1+ae_sqr(v1-ve, _state);
                rms2 = rms2+ae_sqr(v2-ve, _state);
            }
            *idwerrors = *idwerrors||ae_fp_greater(rms2,rms1);
        }
    }
    ae_frame_leave(_state);
}



static void testratintunit_poldiff2(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     double* p,
     double* dp,
     double* d2p,
     ae_state *_state);
static void testratintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state);
static ae_bool testratintunit_is1dsolution(ae_int_t n,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     double c,
     ae_state *_state);





ae_bool testratint(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool bcerrors;
    ae_bool nperrors;
    double threshold;
    double lipschitztol;
    ae_int_t maxn;
    ae_int_t passcount;
    barycentricinterpolant b1;
    barycentricinterpolant b2;
    ae_vector x;
    ae_vector x2;
    ae_vector y;
    ae_vector y2;
    ae_vector w;
    ae_vector w2;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    double h;
    double s1;
    double s2;
    ae_int_t n;
    ae_int_t n2;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t d;
    ae_int_t pass;
    double maxerr;
    double t;
    double a;
    double b;
    double s;
    double v0;
    double v1;
    double v2;
    double v3;
    double d0;
    double d1;
    double d2;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    _barycentricinterpolant_init(&b1, _state, ae_true);
    _barycentricinterpolant_init(&b2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);

    nperrors = ae_false;
    bcerrors = ae_false;
    waserrors = ae_false;
    
    /*
     * PassCount        number of repeated passes
     * Threshold        error tolerance
     * LipschitzTol     Lipschitz constant increase allowed
     *                  when calculating constant on a twice denser grid
     */
    passcount = 5;
    maxn = 15;
    threshold = 1000000*ae_machineepsilon;
    lipschitztol = 1.3;
    
    /*
     * Basic barycentric functions
     */
    for(n=1; n<=10; n++)
    {
        
        /*
         * randomized tests
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * generate weights from polynomial interpolation
             */
            v0 = 1+0.4*ae_randomreal(_state)-0.2;
            v1 = 2*ae_randomreal(_state)-1;
            v2 = 2*ae_randomreal(_state)-1;
            v3 = 2*ae_randomreal(_state)-1;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( n==1 )
                {
                    x.ptr.p_double[i] = 0;
                }
                else
                {
                    x.ptr.p_double[i] = v0*ae_cos(i*ae_pi/(n-1), _state);
                }
                y.ptr.p_double[i] = ae_sin(v1*x.ptr.p_double[i], _state)+ae_cos(v2*x.ptr.p_double[i], _state)+ae_exp(v3*x.ptr.p_double[i], _state);
            }
            for(j=0; j<=n-1; j++)
            {
                w.ptr.p_double[j] = 1;
                for(k=0; k<=n-1; k++)
                {
                    if( k!=j )
                    {
                        w.ptr.p_double[j] = w.ptr.p_double[j]/(x.ptr.p_double[j]-x.ptr.p_double[k]);
                    }
                }
            }
            barycentricbuildxyw(&x, &y, &w, n, &b1, _state);
            
            /*
             * unpack, then pack again and compare
             */
            testratintunit_brcunset(&b2, _state);
            barycentricunpack(&b1, &n2, &x2, &y2, &w2, _state);
            bcerrors = bcerrors||n2!=n;
            barycentricbuildxyw(&x2, &y2, &w2, n2, &b2, _state);
            t = 2*ae_randomreal(_state)-1;
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            
            /*
             * copy, compare
             */
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            t = 2*ae_randomreal(_state)-1;
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            
            /*
             * test interpolation properties
             */
            for(i=0; i<=n-1; i++)
            {
                
                /*
                 * test interpolation at nodes
                 */
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, x.ptr.p_double[i], _state)-y.ptr.p_double[i], _state),threshold*ae_fabs(y.ptr.p_double[i], _state));
                
                /*
                 * compare with polynomial interpolation
                 */
                t = 2*ae_randomreal(_state)-1;
                testratintunit_poldiff2(&x, &y, n, t, &v0, &v1, &v2, _state);
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, t, _state)-v0, _state),threshold*ae_maxreal(ae_fabs(v0, _state), 1, _state));
                
                /*
                 * test continuity between nodes
                 * calculate Lipschitz constant on two grids -
                 * dense and even more dense. If Lipschitz constant
                 * on a denser grid is significantly increased,
                 * continuity test is failed
                 */
                t = 3.0;
                k = 100;
                s1 = 0;
                for(j=0; j<=k-1; j++)
                {
                    v1 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*j/k;
                    v2 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*(j+1)/k;
                    s1 = ae_maxreal(s1, ae_fabs(barycentriccalc(&b1, v2, _state)-barycentriccalc(&b1, v1, _state), _state)/ae_fabs(v2-v1, _state), _state);
                }
                k = 2*k;
                s2 = 0;
                for(j=0; j<=k-1; j++)
                {
                    v1 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*j/k;
                    v2 = x.ptr.p_double[i]+(t-x.ptr.p_double[i])*(j+1)/k;
                    s2 = ae_maxreal(s2, ae_fabs(barycentriccalc(&b1, v2, _state)-barycentriccalc(&b1, v1, _state), _state)/ae_fabs(v2-v1, _state), _state);
                }
                bcerrors = bcerrors||(ae_fp_greater(s2,lipschitztol*s1)&&ae_fp_greater(s1,threshold*k));
            }
            
            /*
             * test differentiation properties
             */
            for(i=0; i<=n-1; i++)
            {
                t = 2*ae_randomreal(_state)-1;
                testratintunit_poldiff2(&x, &y, n, t, &v0, &v1, &v2, _state);
                d0 = 0;
                d1 = 0;
                d2 = 0;
                barycentricdiff1(&b1, t, &d0, &d1, _state);
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v0-d0, _state),threshold*ae_maxreal(ae_fabs(v0, _state), 1, _state));
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v1-d1, _state),threshold*ae_maxreal(ae_fabs(v1, _state), 1, _state));
                d0 = 0;
                d1 = 0;
                d2 = 0;
                barycentricdiff2(&b1, t, &d0, &d1, &d2, _state);
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v0-d0, _state),threshold*ae_maxreal(ae_fabs(v0, _state), 1, _state));
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v1-d1, _state),threshold*ae_maxreal(ae_fabs(v1, _state), 1, _state));
                bcerrors = bcerrors||ae_fp_greater(ae_fabs(v2-d2, _state),ae_sqrt(threshold, _state)*ae_maxreal(ae_fabs(v2, _state), 1, _state));
            }
            
            /*
             * test linear translation
             */
            t = 2*ae_randomreal(_state)-1;
            a = 2*ae_randomreal(_state)-1;
            b = 2*ae_randomreal(_state)-1;
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            barycentriclintransx(&b2, a, b, _state);
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, a*t+b, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            a = 0;
            b = 2*ae_randomreal(_state)-1;
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            barycentriclintransx(&b2, a, b, _state);
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, a*t+b, _state)-barycentriccalc(&b2, t, _state), _state),threshold);
            a = 2*ae_randomreal(_state)-1;
            b = 2*ae_randomreal(_state)-1;
            testratintunit_brcunset(&b2, _state);
            barycentriccopy(&b1, &b2, _state);
            barycentriclintransy(&b2, a, b, _state);
            bcerrors = bcerrors||ae_fp_greater(ae_fabs(a*barycentriccalc(&b1, t, _state)+b-barycentriccalc(&b2, t, _state), _state),threshold);
        }
    }
    for(pass=0; pass<=3; pass++)
    {
        
        /*
         * Crash-test: small numbers, large numbers
         */
        ae_vector_set_length(&x, 4, _state);
        ae_vector_set_length(&y, 4, _state);
        ae_vector_set_length(&w, 4, _state);
        h = 1;
        if( pass%2==0 )
        {
            h = 100*ae_minrealnumber;
        }
        if( pass%2==1 )
        {
            h = 0.01*ae_maxrealnumber;
        }
        x.ptr.p_double[0] = 0*h;
        x.ptr.p_double[1] = 1*h;
        x.ptr.p_double[2] = 2*h;
        x.ptr.p_double[3] = 3*h;
        y.ptr.p_double[0] = 0*h;
        y.ptr.p_double[1] = 1*h;
        y.ptr.p_double[2] = 2*h;
        y.ptr.p_double[3] = 3*h;
        w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
        w.ptr.p_double[1] = 1*(1/(x.ptr.p_double[1]-x.ptr.p_double[0])+1/(x.ptr.p_double[2]-x.ptr.p_double[1]));
        w.ptr.p_double[2] = -1*(1/(x.ptr.p_double[2]-x.ptr.p_double[1])+1/(x.ptr.p_double[3]-x.ptr.p_double[2]));
        w.ptr.p_double[3] = 1/(x.ptr.p_double[3]-x.ptr.p_double[2]);
        if( pass/2==0 )
        {
            v0 = 0;
        }
        if( pass/2==1 )
        {
            v0 = 0.6*h;
        }
        barycentricbuildxyw(&x, &y, &w, 4, &b1, _state);
        t = barycentriccalc(&b1, v0, _state);
        d0 = 0;
        d1 = 0;
        d2 = 0;
        barycentricdiff1(&b1, v0, &d0, &d1, _state);
        bcerrors = bcerrors||ae_fp_greater(ae_fabs(t-v0, _state),threshold*v0);
        bcerrors = bcerrors||ae_fp_greater(ae_fabs(d0-v0, _state),threshold*v0);
        bcerrors = bcerrors||ae_fp_greater(ae_fabs(d1-1, _state),1000*threshold);
    }
    
    /*
     * crash test: large abscissas, small argument
     *
     * test for errors in D0 is not very strict
     * because renormalization used in Diff1()
     * destroys part of precision.
     */
    ae_vector_set_length(&x, 4, _state);
    ae_vector_set_length(&y, 4, _state);
    ae_vector_set_length(&w, 4, _state);
    h = 0.01*ae_maxrealnumber;
    x.ptr.p_double[0] = 0*h;
    x.ptr.p_double[1] = 1*h;
    x.ptr.p_double[2] = 2*h;
    x.ptr.p_double[3] = 3*h;
    y.ptr.p_double[0] = 0*h;
    y.ptr.p_double[1] = 1*h;
    y.ptr.p_double[2] = 2*h;
    y.ptr.p_double[3] = 3*h;
    w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
    w.ptr.p_double[1] = 1*(1/(x.ptr.p_double[1]-x.ptr.p_double[0])+1/(x.ptr.p_double[2]-x.ptr.p_double[1]));
    w.ptr.p_double[2] = -1*(1/(x.ptr.p_double[2]-x.ptr.p_double[1])+1/(x.ptr.p_double[3]-x.ptr.p_double[2]));
    w.ptr.p_double[3] = 1/(x.ptr.p_double[3]-x.ptr.p_double[2]);
    v0 = 100*ae_minrealnumber;
    barycentricbuildxyw(&x, &y, &w, 4, &b1, _state);
    t = barycentriccalc(&b1, v0, _state);
    d0 = 0;
    d1 = 0;
    d2 = 0;
    barycentricdiff1(&b1, v0, &d0, &d1, _state);
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(t, _state),v0*(1+threshold));
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(d0, _state),v0*(1+threshold));
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(d1-1, _state),1000*threshold);
    
    /*
     * crash test: test safe barycentric formula
     */
    ae_vector_set_length(&x, 4, _state);
    ae_vector_set_length(&y, 4, _state);
    ae_vector_set_length(&w, 4, _state);
    h = 2*ae_minrealnumber;
    x.ptr.p_double[0] = 0*h;
    x.ptr.p_double[1] = 1*h;
    x.ptr.p_double[2] = 2*h;
    x.ptr.p_double[3] = 3*h;
    y.ptr.p_double[0] = 0*h;
    y.ptr.p_double[1] = 1*h;
    y.ptr.p_double[2] = 2*h;
    y.ptr.p_double[3] = 3*h;
    w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
    w.ptr.p_double[1] = 1*(1/(x.ptr.p_double[1]-x.ptr.p_double[0])+1/(x.ptr.p_double[2]-x.ptr.p_double[1]));
    w.ptr.p_double[2] = -1*(1/(x.ptr.p_double[2]-x.ptr.p_double[1])+1/(x.ptr.p_double[3]-x.ptr.p_double[2]));
    w.ptr.p_double[3] = 1/(x.ptr.p_double[3]-x.ptr.p_double[2]);
    v0 = ae_minrealnumber;
    barycentricbuildxyw(&x, &y, &w, 4, &b1, _state);
    t = barycentriccalc(&b1, v0, _state);
    bcerrors = bcerrors||ae_fp_greater(ae_fabs(t-v0, _state)/v0,threshold);
    
    /*
     * Testing "No Poles" interpolation
     */
    maxerr = 0;
    for(pass=1; pass<=passcount-1; pass++)
    {
        ae_vector_set_length(&x, 1, _state);
        ae_vector_set_length(&y, 1, _state);
        x.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        y.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
        barycentricbuildfloaterhormann(&x, &y, 1, 1, &b1, _state);
        maxerr = ae_maxreal(maxerr, ae_fabs(barycentriccalc(&b1, 2*ae_randomreal(_state)-1, _state)-y.ptr.p_double[0], _state), _state);
    }
    for(n=2; n<=10; n++)
    {
        
        /*
         * compare interpolant built by subroutine
         * with interpolant built by hands
         */
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&y, n, _state);
        ae_vector_set_length(&w, n, _state);
        ae_vector_set_length(&w2, n, _state);
        
        /*
         * D=1, non-equidistant nodes
         */
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Initialize X, Y, W
             */
            a = -1-1*ae_randomreal(_state);
            b = 1+1*ae_randomreal(_state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = ae_atan((b-a)*i/(n-1)+a, _state);
            }
            for(i=0; i<=n-1; i++)
            {
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            w.ptr.p_double[0] = -1/(x.ptr.p_double[1]-x.ptr.p_double[0]);
            s = 1;
            for(i=1; i<=n-2; i++)
            {
                w.ptr.p_double[i] = s*(1/(x.ptr.p_double[i]-x.ptr.p_double[i-1])+1/(x.ptr.p_double[i+1]-x.ptr.p_double[i]));
                s = -s;
            }
            w.ptr.p_double[n-1] = s/(x.ptr.p_double[n-1]-x.ptr.p_double[n-2]);
            for(i=0; i<=n-1; i++)
            {
                k = ae_randominteger(n, _state);
                if( k!=i )
                {
                    t = x.ptr.p_double[i];
                    x.ptr.p_double[i] = x.ptr.p_double[k];
                    x.ptr.p_double[k] = t;
                    t = y.ptr.p_double[i];
                    y.ptr.p_double[i] = y.ptr.p_double[k];
                    y.ptr.p_double[k] = t;
                    t = w.ptr.p_double[i];
                    w.ptr.p_double[i] = w.ptr.p_double[k];
                    w.ptr.p_double[k] = t;
                }
            }
            
            /*
             * Build and test
             */
            barycentricbuildfloaterhormann(&x, &y, n, 1, &b1, _state);
            barycentricbuildxyw(&x, &y, &w, n, &b2, _state);
            for(i=1; i<=2*n; i++)
            {
                t = a+(b-a)*ae_randomreal(_state);
                maxerr = ae_maxreal(maxerr, ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state), _state);
            }
        }
        
        /*
         * D = 0, 1, 2. Equidistant nodes.
         */
        for(d=0; d<=2; d++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Skip incorrect (N,D) pairs
                 */
                if( n<2*d )
                {
                    continue;
                }
                
                /*
                 * Initialize X, Y, W
                 */
                a = -1-1*ae_randomreal(_state);
                b = 1+1*ae_randomreal(_state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = (b-a)*i/(n-1)+a;
                }
                for(i=0; i<=n-1; i++)
                {
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                s = 1;
                if( d==0 )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        w.ptr.p_double[i] = s;
                        s = -s;
                    }
                }
                if( d==1 )
                {
                    w.ptr.p_double[0] = -s;
                    for(i=1; i<=n-2; i++)
                    {
                        w.ptr.p_double[i] = 2*s;
                        s = -s;
                    }
                    w.ptr.p_double[n-1] = s;
                }
                if( d==2 )
                {
                    w.ptr.p_double[0] = s;
                    w.ptr.p_double[1] = -3*s;
                    for(i=2; i<=n-3; i++)
                    {
                        w.ptr.p_double[i] = 4*s;
                        s = -s;
                    }
                    w.ptr.p_double[n-2] = 3*s;
                    w.ptr.p_double[n-1] = -s;
                }
                
                /*
                 * Mix
                 */
                for(i=0; i<=n-1; i++)
                {
                    k = ae_randominteger(n, _state);
                    if( k!=i )
                    {
                        t = x.ptr.p_double[i];
                        x.ptr.p_double[i] = x.ptr.p_double[k];
                        x.ptr.p_double[k] = t;
                        t = y.ptr.p_double[i];
                        y.ptr.p_double[i] = y.ptr.p_double[k];
                        y.ptr.p_double[k] = t;
                        t = w.ptr.p_double[i];
                        w.ptr.p_double[i] = w.ptr.p_double[k];
                        w.ptr.p_double[k] = t;
                    }
                }
                
                /*
                 * Build and test
                 */
                barycentricbuildfloaterhormann(&x, &y, n, d, &b1, _state);
                barycentricbuildxyw(&x, &y, &w, n, &b2, _state);
                for(i=1; i<=2*n; i++)
                {
                    t = a+(b-a)*ae_randomreal(_state);
                    maxerr = ae_maxreal(maxerr, ae_fabs(barycentriccalc(&b1, t, _state)-barycentriccalc(&b2, t, _state), _state), _state);
                }
            }
        }
    }
    if( ae_fp_greater(maxerr,threshold) )
    {
        nperrors = ae_true;
    }
    
    /*
     * report
     */
    waserrors = bcerrors||nperrors;
    if( !silent )
    {
        printf("TESTING RATIONAL INTERPOLATION\n");
        printf("BASIC BARYCENTRIC FUNCTIONS:             ");
        if( bcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("FLOATER-HORMANN:                         ");
        if( nperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testratintunit_poldiff2(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     double* p,
     double* dp,
     double* d2p,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector _f;
    ae_int_t m;
    ae_int_t i;
    ae_vector df;
    ae_vector d2f;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init_copy(&_f, f, _state, ae_true);
    f = &_f;
    *p = 0;
    *dp = 0;
    *d2p = 0;
    ae_vector_init(&df, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d2f, 0, DT_REAL, _state, ae_true);

    n = n-1;
    ae_vector_set_length(&df, n+1, _state);
    ae_vector_set_length(&d2f, n+1, _state);
    for(i=0; i<=n; i++)
    {
        d2f.ptr.p_double[i] = 0;
        df.ptr.p_double[i] = 0;
    }
    for(m=1; m<=n; m++)
    {
        for(i=0; i<=n-m; i++)
        {
            d2f.ptr.p_double[i] = ((t-x->ptr.p_double[i+m])*d2f.ptr.p_double[i]+(x->ptr.p_double[i]-t)*d2f.ptr.p_double[i+1]+2*df.ptr.p_double[i]-2*df.ptr.p_double[i+1])/(x->ptr.p_double[i]-x->ptr.p_double[i+m]);
            df.ptr.p_double[i] = ((t-x->ptr.p_double[i+m])*df.ptr.p_double[i]+f->ptr.p_double[i]+(x->ptr.p_double[i]-t)*df.ptr.p_double[i+1]-f->ptr.p_double[i+1])/(x->ptr.p_double[i]-x->ptr.p_double[i+m]);
            f->ptr.p_double[i] = ((t-x->ptr.p_double[i+m])*f->ptr.p_double[i]+(x->ptr.p_double[i]-t)*f->ptr.p_double[i+1])/(x->ptr.p_double[i]-x->ptr.p_double[i+m]);
        }
    }
    *p = f->ptr.p_double[0];
    *dp = df.ptr.p_double[0];
    *d2p = d2f.ptr.p_double[0];
    ae_frame_leave(_state);
}


static void testratintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_vector w;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 1, _state);
    ae_vector_set_length(&y, 1, _state);
    ae_vector_set_length(&w, 1, _state);
    x.ptr.p_double[0] = 0;
    y.ptr.p_double[0] = 0;
    w.ptr.p_double[0] = 1;
    barycentricbuildxyw(&x, &y, &w, 1, b, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Tests whether constant C is solution of 1D LLS problem
*************************************************************************/
static ae_bool testratintunit_is1dsolution(ae_int_t n,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     double c,
     ae_state *_state)
{
    ae_int_t i;
    double s1;
    double s2;
    double s3;
    double delta;
    ae_bool result;


    delta = 0.001;
    
    /*
     * Test result
     */
    s1 = 0;
    for(i=0; i<=n-1; i++)
    {
        s1 = s1+ae_sqr(w->ptr.p_double[i]*(c-y->ptr.p_double[i]), _state);
    }
    s2 = 0;
    s3 = 0;
    for(i=0; i<=n-1; i++)
    {
        s2 = s2+ae_sqr(w->ptr.p_double[i]*(c+delta-y->ptr.p_double[i]), _state);
        s3 = s3+ae_sqr(w->ptr.p_double[i]*(c-delta-y->ptr.p_double[i]), _state);
    }
    result = ae_fp_greater_eq(s2,s1)&&ae_fp_greater_eq(s3,s1);
    return result;
}



static double testpolintunit_internalpolint(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     ae_state *_state);
static void testpolintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state);





/*************************************************************************
Unit test
*************************************************************************/
ae_bool testpolint(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool interrors;
    double threshold;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector c;
    ae_vector x2;
    ae_vector y2;
    ae_vector w2;
    ae_vector xfull;
    ae_vector yfull;
    double a;
    double b;
    double t;
    ae_int_t i;
    ae_int_t k;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    double v;
    double v0;
    double v1;
    double v2;
    double v3;
    double v4;
    double pscale;
    double poffset;
    barycentricinterpolant p;
    barycentricinterpolant p1;
    barycentricinterpolant p2;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _barycentricinterpolant_init(&p, _state, ae_true);
    _barycentricinterpolant_init(&p1, _state, ae_true);
    _barycentricinterpolant_init(&p2, _state, ae_true);

    waserrors = ae_false;
    interrors = ae_false;
    maxn = 5;
    passcount = 20;
    threshold = 1.0E8*ae_machineepsilon;
    
    /*
     * Test equidistant interpolation
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * prepare task:
             * * equidistant points
             * * random Y
             * * T in [A,B] or near (within 10% of its width)
             */
            do
            {
                a = 2*ae_randomreal(_state)-1;
                b = 2*ae_randomreal(_state)-1;
            }
            while(ae_fp_less_eq(ae_fabs(a-b, _state),0.2));
            t = a+(1.2*ae_randomreal(_state)-0.1)*(b-a);
            taskgenint1dequidist(a, b, n, &x, &y, _state);
            
            /*
             * test "fast" equidistant interpolation (no barycentric model)
             */
            interrors = interrors||ae_fp_greater(ae_fabs(polynomialcalceqdist(a, b, &y, n, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "slow" equidistant interpolation (create barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuild(&x, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "fast" interpolation (create "fast" barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuildeqdist(a, b, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
        }
    }
    
    /*
     * Test Chebyshev-1 interpolation
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * prepare task:
             * * equidistant points
             * * random Y
             * * T in [A,B] or near (within 10% of its width)
             */
            do
            {
                a = 2*ae_randomreal(_state)-1;
                b = 2*ae_randomreal(_state)-1;
            }
            while(ae_fp_less_eq(ae_fabs(a-b, _state),0.2));
            t = a+(1.2*ae_randomreal(_state)-0.1)*(b-a);
            taskgenint1dcheb1(a, b, n, &x, &y, _state);
            
            /*
             * test "fast" interpolation (no barycentric model)
             */
            interrors = interrors||ae_fp_greater(ae_fabs(polynomialcalccheb1(a, b, &y, n, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "slow" interpolation (create barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuild(&x, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "fast" interpolation (create "fast" barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuildcheb1(a, b, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
        }
    }
    
    /*
     * Test Chebyshev-2 interpolation
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * prepare task:
             * * equidistant points
             * * random Y
             * * T in [A,B] or near (within 10% of its width)
             */
            do
            {
                a = 2*ae_randomreal(_state)-1;
                b = 2*ae_randomreal(_state)-1;
            }
            while(ae_fp_less_eq(ae_fabs(a-b, _state),0.2));
            t = a+(1.2*ae_randomreal(_state)-0.1)*(b-a);
            taskgenint1dcheb2(a, b, n, &x, &y, _state);
            
            /*
             * test "fast" interpolation (no barycentric model)
             */
            interrors = interrors||ae_fp_greater(ae_fabs(polynomialcalccheb2(a, b, &y, n, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "slow" interpolation (create barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuild(&x, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
            
            /*
             * test "fast" interpolation (create "fast" barycentric model)
             */
            testpolintunit_brcunset(&p, _state);
            polynomialbuildcheb2(a, b, &y, n, &p, _state);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, t, _state)-testpolintunit_internalpolint(&x, &y, n, t, _state), _state),threshold);
        }
    }
    
    /*
     * Testing conversion Barycentric<->Chebyshev
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(k=1; k<=3; k++)
        {
            
            /*
             * Allocate
             */
            ae_vector_set_length(&x, k, _state);
            ae_vector_set_length(&y, k, _state);
            
            /*
             * Generate problem
             */
            a = 2*ae_randomreal(_state)-1;
            b = a+(0.1+ae_randomreal(_state))*(2*ae_randominteger(2, _state)-1);
            v0 = 2*ae_randomreal(_state)-1;
            v1 = 2*ae_randomreal(_state)-1;
            v2 = 2*ae_randomreal(_state)-1;
            if( k==1 )
            {
                x.ptr.p_double[0] = 0.5*(a+b);
                y.ptr.p_double[0] = v0;
            }
            if( k==2 )
            {
                x.ptr.p_double[0] = a;
                y.ptr.p_double[0] = v0-v1;
                x.ptr.p_double[1] = b;
                y.ptr.p_double[1] = v0+v1;
            }
            if( k==3 )
            {
                x.ptr.p_double[0] = a;
                y.ptr.p_double[0] = v0-v1+v2;
                x.ptr.p_double[1] = 0.5*(a+b);
                y.ptr.p_double[1] = v0-v2;
                x.ptr.p_double[2] = b;
                y.ptr.p_double[2] = v0+v1+v2;
            }
            
            /*
             * Test forward conversion
             */
            polynomialbuild(&x, &y, k, &p, _state);
            ae_vector_set_length(&c, 1, _state);
            polynomialbar2cheb(&p, a, b, &c, _state);
            interrors = interrors||c.cnt!=k;
            if( k>=1 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v0, _state),threshold);
            }
            if( k>=2 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[1]-v1, _state),threshold);
            }
            if( k>=3 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[2]-v2, _state),threshold);
            }
            
            /*
             * Test backward conversion
             */
            polynomialcheb2bar(&c, k, a, b, &p2, _state);
            v = a+ae_randomreal(_state)*(b-a);
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, v, _state)-barycentriccalc(&p2, v, _state), _state),threshold);
        }
    }
    
    /*
     * Testing conversion Barycentric<->Power
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(k=1; k<=5; k++)
        {
            
            /*
             * Allocate
             */
            ae_vector_set_length(&x, k, _state);
            ae_vector_set_length(&y, k, _state);
            
            /*
             * Generate problem
             */
            poffset = 2*ae_randomreal(_state)-1;
            pscale = (0.1+ae_randomreal(_state))*(2*ae_randominteger(2, _state)-1);
            v0 = 2*ae_randomreal(_state)-1;
            v1 = 2*ae_randomreal(_state)-1;
            v2 = 2*ae_randomreal(_state)-1;
            v3 = 2*ae_randomreal(_state)-1;
            v4 = 2*ae_randomreal(_state)-1;
            if( k==1 )
            {
                x.ptr.p_double[0] = poffset;
                y.ptr.p_double[0] = v0;
            }
            if( k==2 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1;
                x.ptr.p_double[1] = poffset+pscale;
                y.ptr.p_double[1] = v0+v1;
            }
            if( k==3 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1+v2;
                x.ptr.p_double[1] = poffset;
                y.ptr.p_double[1] = v0;
                x.ptr.p_double[2] = poffset+pscale;
                y.ptr.p_double[2] = v0+v1+v2;
            }
            if( k==4 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1+v2-v3;
                x.ptr.p_double[1] = poffset-0.5*pscale;
                y.ptr.p_double[1] = v0-0.5*v1+0.25*v2-0.125*v3;
                x.ptr.p_double[2] = poffset+0.5*pscale;
                y.ptr.p_double[2] = v0+0.5*v1+0.25*v2+0.125*v3;
                x.ptr.p_double[3] = poffset+pscale;
                y.ptr.p_double[3] = v0+v1+v2+v3;
            }
            if( k==5 )
            {
                x.ptr.p_double[0] = poffset-pscale;
                y.ptr.p_double[0] = v0-v1+v2-v3+v4;
                x.ptr.p_double[1] = poffset-0.5*pscale;
                y.ptr.p_double[1] = v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4;
                x.ptr.p_double[2] = poffset;
                y.ptr.p_double[2] = v0;
                x.ptr.p_double[3] = poffset+0.5*pscale;
                y.ptr.p_double[3] = v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4;
                x.ptr.p_double[4] = poffset+pscale;
                y.ptr.p_double[4] = v0+v1+v2+v3+v4;
            }
            
            /*
             * Test forward conversion
             */
            polynomialbuild(&x, &y, k, &p, _state);
            ae_vector_set_length(&c, 1, _state);
            polynomialbar2pow(&p, poffset, pscale, &c, _state);
            interrors = interrors||c.cnt!=k;
            if( k>=1 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v0, _state),threshold);
            }
            if( k>=2 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[1]-v1, _state),threshold);
            }
            if( k>=3 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[2]-v2, _state),threshold);
            }
            if( k>=4 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[3]-v3, _state),threshold);
            }
            if( k>=5 )
            {
                interrors = interrors||ae_fp_greater(ae_fabs(c.ptr.p_double[4]-v4, _state),threshold);
            }
            
            /*
             * Test backward conversion
             */
            polynomialpow2bar(&c, k, poffset, pscale, &p2, _state);
            v = poffset+(2*ae_randomreal(_state)-1)*pscale;
            interrors = interrors||ae_fp_greater(ae_fabs(barycentriccalc(&p, v, _state)-barycentriccalc(&p2, v, _state), _state),threshold);
        }
    }
    
    /*
     * crash-test: ability to solve tasks which will overflow/underflow
     * weights with straightforward implementation
     */
    for(n=1; n<=20; n++)
    {
        a = -0.1*ae_maxrealnumber;
        b = 0.1*ae_maxrealnumber;
        taskgenint1dequidist(a, b, n, &x, &y, _state);
        polynomialbuild(&x, &y, n, &p, _state);
        for(i=0; i<=n-1; i++)
        {
            interrors = interrors||ae_fp_eq(p.w.ptr.p_double[i],0);
        }
    }
    
    /*
     * report
     */
    waserrors = interrors;
    if( !silent )
    {
        printf("TESTING POLYNOMIAL INTERPOLATION\n");
        
        /*
         * Normal tests
         */
        printf("INTERPOLATION TEST:                      ");
        if( interrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static double testpolintunit_internalpolint(/* Real    */ ae_vector* x,
     /* Real    */ ae_vector* f,
     ae_int_t n,
     double t,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector _f;
    ae_int_t i;
    ae_int_t j;
    double result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init_copy(&_f, f, _state, ae_true);
    f = &_f;

    n = n-1;
    for(j=0; j<=n-1; j++)
    {
        for(i=j+1; i<=n; i++)
        {
            f->ptr.p_double[i] = ((t-x->ptr.p_double[j])*f->ptr.p_double[i]-(t-x->ptr.p_double[i])*f->ptr.p_double[j])/(x->ptr.p_double[i]-x->ptr.p_double[j]);
        }
    }
    result = f->ptr.p_double[n];
    ae_frame_leave(_state);
    return result;
}


static void testpolintunit_brcunset(barycentricinterpolant* b,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_vector w;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 1, _state);
    ae_vector_set_length(&y, 1, _state);
    ae_vector_set_length(&w, 1, _state);
    x.ptr.p_double[0] = 0;
    y.ptr.p_double[0] = 0;
    w.ptr.p_double[0] = 1;
    barycentricbuildxyw(&x, &y, &w, 1, b, _state);
    ae_frame_leave(_state);
}



static void testspline1dunit_lconst(double a,
     double b,
     spline1dinterpolant* c,
     double lstep,
     double* l0,
     double* l1,
     double* l2,
     ae_state *_state);
static ae_bool testspline1dunit_testunpack(spline1dinterpolant* c,
     /* Real    */ ae_vector* x,
     ae_state *_state);
static void testspline1dunit_unsetspline1d(spline1dinterpolant* c,
     ae_state *_state);
static void testspline1dunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state);
static ae_bool testspline1dunit_is1dsolution(ae_int_t n,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     double c,
     ae_state *_state);





ae_bool testspline1d(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool crserrors;
    ae_bool cserrors;
    ae_bool hserrors;
    ae_bool aserrors;
    ae_bool lserrors;
    ae_bool dserrors;
    ae_bool uperrors;
    ae_bool cperrors;
    ae_bool lterrors;
    ae_bool ierrors;
    double nonstrictthreshold;
    double threshold;
    ae_int_t passcount;
    double lstep;
    double h;
    ae_int_t maxn;
    ae_int_t bltype;
    ae_int_t brtype;
    ae_bool periodiccond;
    ae_int_t n;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    ae_vector x;
    ae_vector y;
    ae_vector yp;
    ae_vector w;
    ae_vector w2;
    ae_vector y2;
    ae_vector d;
    ae_vector xc;
    ae_vector yc;
    ae_int_t n2;
    ae_vector tmp0;
    ae_vector tmp1;
    ae_vector tmp2;
    ae_vector tmpx;
    ae_vector dc;
    spline1dinterpolant c;
    spline1dinterpolant c2;
    double a;
    double b;
    double bl;
    double br;
    double t;
    double sa;
    double sb;
    double v;
    double l10;
    double l11;
    double l12;
    double l20;
    double l21;
    double l22;
    double p0;
    double p1;
    double p2;
    double s;
    double ds;
    double d2s;
    double s2;
    double ds2;
    double d2s2;
    double vl;
    double vm;
    double vr;
    double err;
    double tension;
    double intab;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yp, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _spline1dinterpolant_init(&c, _state, ae_true);
    _spline1dinterpolant_init(&c2, _state, ae_true);

    waserrors = ae_false;
    passcount = 20;
    lstep = 0.005;
    h = 0.00001;
    maxn = 10;
    threshold = 10000*ae_machineepsilon;
    nonstrictthreshold = 0.00001;
    lserrors = ae_false;
    cserrors = ae_false;
    crserrors = ae_false;
    hserrors = ae_false;
    aserrors = ae_false;
    dserrors = ae_false;
    cperrors = ae_false;
    uperrors = ae_false;
    lterrors = ae_false;
    ierrors = ae_false;
    
    /*
     * General test: linear, cubic, Hermite, Akima
     */
    for(n=2; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        ae_vector_set_length(&yp, n-1+1, _state);
        ae_vector_set_length(&d, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Prepare task:
             * * X contains abscissas from [A,B]
             * * Y contains function values
             * * YP contains periodic function values
             */
            a = -1-ae_randomreal(_state);
            b = 1+ae_randomreal(_state);
            bl = 2*ae_randomreal(_state)-1;
            br = 2*ae_randomreal(_state)-1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 0.5*(b+a)+0.5*(b-a)*ae_cos(ae_pi*(2*i+1)/(2*n), _state);
                if( i==0 )
                {
                    x.ptr.p_double[i] = a;
                }
                if( i==n-1 )
                {
                    x.ptr.p_double[i] = b;
                }
                y.ptr.p_double[i] = ae_cos(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
                yp.ptr.p_double[i] = y.ptr.p_double[i];
                d.ptr.p_double[i] = -1.3*ae_pi*ae_sin(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
            }
            yp.ptr.p_double[n-1] = yp.ptr.p_double[0];
            for(i=0; i<=n-1; i++)
            {
                k = ae_randominteger(n, _state);
                if( k!=i )
                {
                    t = x.ptr.p_double[i];
                    x.ptr.p_double[i] = x.ptr.p_double[k];
                    x.ptr.p_double[k] = t;
                    t = y.ptr.p_double[i];
                    y.ptr.p_double[i] = y.ptr.p_double[k];
                    y.ptr.p_double[k] = t;
                    t = yp.ptr.p_double[i];
                    yp.ptr.p_double[i] = yp.ptr.p_double[k];
                    yp.ptr.p_double[k] = t;
                    t = d.ptr.p_double[i];
                    d.ptr.p_double[i] = d.ptr.p_double[k];
                    d.ptr.p_double[k] = t;
                }
            }
            
            /*
             * Build linear spline
             * Test for general interpolation scheme properties:
             * * values at nodes
             * * continuous function
             * Test for specific properties is implemented below.
             */
            spline1dbuildlinear(&x, &y, n, &c, _state);
            err = 0;
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
            }
            lserrors = lserrors||ae_fp_greater(err,threshold);
            testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
            testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
            lserrors = lserrors||ae_fp_greater(l20/l10,1.2);
            
            /*
             * Build cubic spline.
             * Test for interpolation scheme properties:
             * * values at nodes
             * * boundary conditions
             * * continuous function
             * * continuous first derivative
             * * continuous second derivative
             * * periodicity properties
             * * Spline1DGridDiff(), Spline1DGridDiff2() and Spline1DDiff()
             *   calls must return same results
             */
            for(bltype=-1; bltype<=2; bltype++)
            {
                for(brtype=-1; brtype<=2; brtype++)
                {
                    
                    /*
                     * skip meaningless combination of boundary conditions
                     * (one condition is periodic, another is not)
                     */
                    periodiccond = bltype==-1||brtype==-1;
                    if( periodiccond&&bltype!=brtype )
                    {
                        continue;
                    }
                    
                    /*
                     * build
                     */
                    if( periodiccond )
                    {
                        spline1dbuildcubic(&x, &yp, n, bltype, bl, brtype, br, &c, _state);
                    }
                    else
                    {
                        spline1dbuildcubic(&x, &y, n, bltype, bl, brtype, br, &c, _state);
                    }
                    
                    /*
                     * interpolation properties
                     */
                    err = 0;
                    if( periodiccond )
                    {
                        
                        /*
                         * * check values at nodes; spline is periodic so
                         *   we add random number of periods to nodes
                         * * we also test for periodicity of derivatives
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            v = x.ptr.p_double[i];
                            vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                            t = yp.ptr.p_double[i]-spline1dcalc(&c, vm, _state);
                            err = ae_maxreal(err, ae_fabs(t, _state), _state);
                            spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                            spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                            err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                            err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                            err = ae_maxreal(err, ae_fabs(d2s-d2s2, _state), _state);
                        }
                        
                        /*
                         * periodicity between nodes
                         */
                        v = a+(b-a)*ae_randomreal(_state);
                        vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                        err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&c, vm, _state), _state), _state);
                        spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                        err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-d2s2, _state), _state);
                    }
                    else
                    {
                        
                        /*
                         * * check values at nodes
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
                        }
                    }
                    cserrors = cserrors||ae_fp_greater(err,threshold);
                    
                    /*
                     * check boundary conditions
                     */
                    err = 0;
                    if( bltype==0 )
                    {
                        spline1ddiff(&c, a-h, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, a+h, &s2, &ds2, &d2s2, _state);
                        t = (d2s2-d2s)/(2*h);
                        err = ae_maxreal(err, ae_fabs(t, _state), _state);
                    }
                    if( bltype==1 )
                    {
                        t = (spline1dcalc(&c, a+h, _state)-spline1dcalc(&c, a-h, _state))/(2*h);
                        err = ae_maxreal(err, ae_fabs(bl-t, _state), _state);
                    }
                    if( bltype==2 )
                    {
                        t = (spline1dcalc(&c, a+h, _state)-2*spline1dcalc(&c, a, _state)+spline1dcalc(&c, a-h, _state))/ae_sqr(h, _state);
                        err = ae_maxreal(err, ae_fabs(bl-t, _state), _state);
                    }
                    if( brtype==0 )
                    {
                        spline1ddiff(&c, b-h, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, b+h, &s2, &ds2, &d2s2, _state);
                        t = (d2s2-d2s)/(2*h);
                        err = ae_maxreal(err, ae_fabs(t, _state), _state);
                    }
                    if( brtype==1 )
                    {
                        t = (spline1dcalc(&c, b+h, _state)-spline1dcalc(&c, b-h, _state))/(2*h);
                        err = ae_maxreal(err, ae_fabs(br-t, _state), _state);
                    }
                    if( brtype==2 )
                    {
                        t = (spline1dcalc(&c, b+h, _state)-2*spline1dcalc(&c, b, _state)+spline1dcalc(&c, b-h, _state))/ae_sqr(h, _state);
                        err = ae_maxreal(err, ae_fabs(br-t, _state), _state);
                    }
                    if( bltype==-1||brtype==-1 )
                    {
                        spline1ddiff(&c, a+100*ae_machineepsilon, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, b-100*ae_machineepsilon, &s2, &ds2, &d2s2, _state);
                        err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-d2s2, _state), _state);
                    }
                    cserrors = cserrors||ae_fp_greater(err,1.0E-3);
                    
                    /*
                     * Check Lipschitz continuity
                     */
                    testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
                    testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
                    if( ae_fp_greater(l10,1.0E-6) )
                    {
                        cserrors = cserrors||ae_fp_greater(l20/l10,1.2);
                    }
                    if( ae_fp_greater(l11,1.0E-6) )
                    {
                        cserrors = cserrors||ae_fp_greater(l21/l11,1.2);
                    }
                    if( ae_fp_greater(l12,1.0E-6) )
                    {
                        cserrors = cserrors||ae_fp_greater(l22/l12,1.2);
                    }
                    
                    /*
                     * compare spline1dgriddiff() and spline1ddiff() results
                     */
                    err = 0;
                    if( periodiccond )
                    {
                        spline1dgriddiffcubic(&x, &yp, n, bltype, bl, brtype, br, &tmp1, _state);
                    }
                    else
                    {
                        spline1dgriddiffcubic(&x, &y, n, bltype, bl, brtype, br, &tmp1, _state);
                    }
                    ae_assert(tmp1.cnt>=n, "Assertion failed", _state);
                    for(i=0; i<=n-1; i++)
                    {
                        spline1ddiff(&c, x.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                    }
                    if( periodiccond )
                    {
                        spline1dgriddiff2cubic(&x, &yp, n, bltype, bl, brtype, br, &tmp1, &tmp2, _state);
                    }
                    else
                    {
                        spline1dgriddiff2cubic(&x, &y, n, bltype, bl, brtype, br, &tmp1, &tmp2, _state);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        spline1ddiff(&c, x.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-tmp2.ptr.p_double[i], _state), _state);
                    }
                    cserrors = cserrors||ae_fp_greater(err,threshold);
                    
                    /*
                     * compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results
                     */
                    n2 = 2+ae_randominteger(2*n, _state);
                    ae_vector_set_length(&tmpx, n2, _state);
                    for(i=0; i<=n2-1; i++)
                    {
                        tmpx.ptr.p_double[i] = 0.5*(a+b)+(a-b)*(2*ae_randomreal(_state)-1);
                    }
                    err = 0;
                    if( periodiccond )
                    {
                        spline1dconvcubic(&x, &yp, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, _state);
                    }
                    else
                    {
                        spline1dconvcubic(&x, &y, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, _state);
                    }
                    for(i=0; i<=n2-1; i++)
                    {
                        spline1ddiff(&c, tmpx.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(s-tmp0.ptr.p_double[i], _state), _state);
                    }
                    if( periodiccond )
                    {
                        spline1dconvdiffcubic(&x, &yp, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, _state);
                    }
                    else
                    {
                        spline1dconvdiffcubic(&x, &y, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, _state);
                    }
                    for(i=0; i<=n2-1; i++)
                    {
                        spline1ddiff(&c, tmpx.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(s-tmp0.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                    }
                    if( periodiccond )
                    {
                        spline1dconvdiff2cubic(&x, &yp, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, &tmp2, _state);
                    }
                    else
                    {
                        spline1dconvdiff2cubic(&x, &y, n, bltype, bl, brtype, br, &tmpx, n2, &tmp0, &tmp1, &tmp2, _state);
                    }
                    for(i=0; i<=n2-1; i++)
                    {
                        spline1ddiff(&c, tmpx.ptr.p_double[i], &s, &ds, &d2s, _state);
                        err = ae_maxreal(err, ae_fabs(s-tmp0.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-tmp1.ptr.p_double[i], _state), _state);
                        err = ae_maxreal(err, ae_fabs(d2s-tmp2.ptr.p_double[i], _state), _state);
                    }
                    cserrors = cserrors||ae_fp_greater(err,threshold);
                }
            }
            
            /*
             * Build Catmull-Rom spline.
             * Test for interpolation scheme properties:
             * * values at nodes
             * * boundary conditions
             * * continuous function
             * * continuous first derivative
             * * periodicity properties
             */
            for(bltype=-1; bltype<=0; bltype++)
            {
                periodiccond = bltype==-1;
                
                /*
                 * select random tension value, then build
                 */
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    if( ae_fp_greater(ae_randomreal(_state),0.5) )
                    {
                        tension = 0;
                    }
                    else
                    {
                        tension = 1;
                    }
                }
                else
                {
                    tension = ae_randomreal(_state);
                }
                if( periodiccond )
                {
                    spline1dbuildcatmullrom(&x, &yp, n, bltype, tension, &c, _state);
                }
                else
                {
                    spline1dbuildcatmullrom(&x, &y, n, bltype, tension, &c, _state);
                }
                
                /*
                 * interpolation properties
                 */
                err = 0;
                if( periodiccond )
                {
                    
                    /*
                     * * check values at nodes; spline is periodic so
                     *   we add random number of periods to nodes
                     * * we also test for periodicity of first derivative
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        v = x.ptr.p_double[i];
                        vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                        t = yp.ptr.p_double[i]-spline1dcalc(&c, vm, _state);
                        err = ae_maxreal(err, ae_fabs(t, _state), _state);
                        spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                        spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                        err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                    }
                    
                    /*
                     * periodicity between nodes
                     */
                    v = a+(b-a)*ae_randomreal(_state);
                    vm = v+(b-a)*(ae_randominteger(5, _state)-2);
                    err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&c, vm, _state), _state), _state);
                    spline1ddiff(&c, v, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, vm, &s2, &ds2, &d2s2, _state);
                    err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                    err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                }
                else
                {
                    
                    /*
                     * * check values at nodes
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
                    }
                }
                crserrors = crserrors||ae_fp_greater(err,threshold);
                
                /*
                 * check boundary conditions
                 */
                err = 0;
                if( bltype==0 )
                {
                    spline1ddiff(&c, a-h, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, a+h, &s2, &ds2, &d2s2, _state);
                    t = (d2s2-d2s)/(2*h);
                    err = ae_maxreal(err, ae_fabs(t, _state), _state);
                    spline1ddiff(&c, b-h, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, b+h, &s2, &ds2, &d2s2, _state);
                    t = (d2s2-d2s)/(2*h);
                    err = ae_maxreal(err, ae_fabs(t, _state), _state);
                }
                if( bltype==-1 )
                {
                    spline1ddiff(&c, a+100*ae_machineepsilon, &s, &ds, &d2s, _state);
                    spline1ddiff(&c, b-100*ae_machineepsilon, &s2, &ds2, &d2s2, _state);
                    err = ae_maxreal(err, ae_fabs(s-s2, _state), _state);
                    err = ae_maxreal(err, ae_fabs(ds-ds2, _state), _state);
                }
                crserrors = crserrors||ae_fp_greater(err,1.0E-3);
                
                /*
                 * Check Lipschitz continuity
                 */
                testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
                testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
                if( ae_fp_greater(l10,1.0E-6) )
                {
                    crserrors = crserrors||ae_fp_greater(l20/l10,1.2);
                }
                if( ae_fp_greater(l11,1.0E-6) )
                {
                    crserrors = crserrors||ae_fp_greater(l21/l11,1.2);
                }
            }
            
            /*
             * Build Hermite spline.
             * Test for interpolation scheme properties:
             * * values and derivatives at nodes
             * * continuous function
             * * continuous first derivative
             */
            spline1dbuildhermite(&x, &y, &d, n, &c, _state);
            err = 0;
            for(i=0; i<=n-1; i++)
            {
                err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
            }
            hserrors = hserrors||ae_fp_greater(err,threshold);
            err = 0;
            for(i=0; i<=n-1; i++)
            {
                t = (spline1dcalc(&c, x.ptr.p_double[i]+h, _state)-spline1dcalc(&c, x.ptr.p_double[i]-h, _state))/(2*h);
                err = ae_maxreal(err, ae_fabs(d.ptr.p_double[i]-t, _state), _state);
            }
            hserrors = hserrors||ae_fp_greater(err,1.0E-3);
            testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
            testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
            hserrors = hserrors||ae_fp_greater(l20/l10,1.2);
            hserrors = hserrors||ae_fp_greater(l21/l11,1.2);
            
            /*
             * Build Akima spline
             * Test for general interpolation scheme properties:
             * * values at nodes
             * * continuous function
             * * continuous first derivative
             * Test for specific properties is implemented below.
             */
            if( n>=5 )
            {
                spline1dbuildakima(&x, &y, n, &c, _state);
                err = 0;
                for(i=0; i<=n-1; i++)
                {
                    err = ae_maxreal(err, ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state), _state);
                }
                aserrors = aserrors||ae_fp_greater(err,threshold);
                testspline1dunit_lconst(a, b, &c, lstep, &l10, &l11, &l12, _state);
                testspline1dunit_lconst(a, b, &c, lstep/3, &l20, &l21, &l22, _state);
                hserrors = hserrors||ae_fp_greater(l20/l10,1.2);
                hserrors = hserrors||ae_fp_greater(l21/l11,1.2);
            }
        }
    }
    
    /*
     * Special linear spline test:
     * test for linearity between x[i] and x[i+1]
     */
    for(n=2; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        
        /*
         * Prepare task
         */
        a = -1;
        b = 1;
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = a+(b-a)*i/(n-1);
            y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        spline1dbuildlinear(&x, &y, n, &c, _state);
        
        /*
         * Test
         */
        err = 0;
        for(k=0; k<=n-2; k++)
        {
            a = x.ptr.p_double[k];
            b = x.ptr.p_double[k+1];
            for(pass=1; pass<=passcount; pass++)
            {
                t = a+(b-a)*ae_randomreal(_state);
                v = y.ptr.p_double[k]+(t-a)/(b-a)*(y.ptr.p_double[k+1]-y.ptr.p_double[k]);
                err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-v, _state), _state);
            }
        }
        lserrors = lserrors||ae_fp_greater(err,threshold);
    }
    
    /*
     * Special Akima test: test outlier sensitivity
     * Spline value at (x[i], x[i+1]) should depend from
     * f[i-2], f[i-1], f[i], f[i+1], f[i+2], f[i+3] only.
     */
    for(n=5; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        ae_vector_set_length(&y2, n-1+1, _state);
        
        /*
         * Prepare unperturbed Akima spline
         */
        a = -1;
        b = 1;
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = a+(b-a)*i/(n-1);
            y.ptr.p_double[i] = ae_cos(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
        }
        spline1dbuildakima(&x, &y, n, &c, _state);
        
        /*
         * Process perturbed tasks
         */
        err = 0;
        for(k=0; k<=n-1; k++)
        {
            ae_v_move(&y2.ptr.p_double[0], 1, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
            y2.ptr.p_double[k] = 5;
            spline1dbuildakima(&x, &y2, n, &c2, _state);
            
            /*
             * Test left part independence
             */
            if( k-3>=1 )
            {
                a = -1;
                b = x.ptr.p_double[k-3];
                for(pass=1; pass<=passcount; pass++)
                {
                    t = a+(b-a)*ae_randomreal(_state);
                    err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-spline1dcalc(&c2, t, _state), _state), _state);
                }
            }
            
            /*
             * Test right part independence
             */
            if( k+3<=n-2 )
            {
                a = x.ptr.p_double[k+3];
                b = 1;
                for(pass=1; pass<=passcount; pass++)
                {
                    t = a+(b-a)*ae_randomreal(_state);
                    err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-spline1dcalc(&c2, t, _state), _state), _state);
                }
            }
        }
        aserrors = aserrors||ae_fp_greater(err,threshold);
    }
    
    /*
     * Differentiation, copy/unpack test
     */
    for(n=2; n<=maxn; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        
        /*
         * Prepare cubic spline
         */
        a = -1-ae_randomreal(_state);
        b = 1+ae_randomreal(_state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = a+(b-a)*i/(n-1);
            y.ptr.p_double[i] = ae_cos(1.3*ae_pi*x.ptr.p_double[i]+0.4, _state);
        }
        spline1dbuildcubic(&x, &y, n, 2, 0.0, 2, 0.0, &c, _state);
        
        /*
         * Test diff
         */
        err = 0;
        for(pass=1; pass<=passcount; pass++)
        {
            t = a+(b-a)*ae_randomreal(_state);
            spline1ddiff(&c, t, &s, &ds, &d2s, _state);
            vl = spline1dcalc(&c, t-h, _state);
            vm = spline1dcalc(&c, t, _state);
            vr = spline1dcalc(&c, t+h, _state);
            err = ae_maxreal(err, ae_fabs(s-vm, _state), _state);
            err = ae_maxreal(err, ae_fabs(ds-(vr-vl)/(2*h), _state), _state);
            err = ae_maxreal(err, ae_fabs(d2s-(vr-2*vm+vl)/ae_sqr(h, _state), _state), _state);
        }
        dserrors = dserrors||ae_fp_greater(err,0.001);
        
        /*
         * Test copy
         */
        testspline1dunit_unsetspline1d(&c2, _state);
        spline1dcopy(&c, &c2, _state);
        err = 0;
        for(pass=1; pass<=passcount; pass++)
        {
            t = a+(b-a)*ae_randomreal(_state);
            err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-spline1dcalc(&c2, t, _state), _state), _state);
        }
        cperrors = cperrors||ae_fp_greater(err,threshold);
        
        /*
         * Test unpack
         */
        uperrors = uperrors||!testspline1dunit_testunpack(&c, &x, _state);
        
        /*
         * Test lin.trans.
         */
        err = 0;
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * LinTransX, general A
             */
            sa = 4*ae_randomreal(_state)-2;
            sb = 2*ae_randomreal(_state)-1;
            t = a+(b-a)*ae_randomreal(_state);
            spline1dcopy(&c, &c2, _state);
            spline1dlintransx(&c2, sa, sb, _state);
            err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, t, _state)-spline1dcalc(&c2, (t-sb)/sa, _state), _state), _state);
            
            /*
             * LinTransX, special case: A=0
             */
            sb = 2*ae_randomreal(_state)-1;
            t = a+(b-a)*ae_randomreal(_state);
            spline1dcopy(&c, &c2, _state);
            spline1dlintransx(&c2, 0, sb, _state);
            err = ae_maxreal(err, ae_fabs(spline1dcalc(&c, sb, _state)-spline1dcalc(&c2, t, _state), _state), _state);
            
            /*
             * LinTransY
             */
            sa = 2*ae_randomreal(_state)-1;
            sb = 2*ae_randomreal(_state)-1;
            t = a+(b-a)*ae_randomreal(_state);
            spline1dcopy(&c, &c2, _state);
            spline1dlintransy(&c2, sa, sb, _state);
            err = ae_maxreal(err, ae_fabs(sa*spline1dcalc(&c, t, _state)+sb-spline1dcalc(&c2, t, _state), _state), _state);
        }
        lterrors = lterrors||ae_fp_greater(err,threshold);
    }
    
    /*
     * Testing integration.
     * Three tests are performed:
     *
     * * approximate test (well behaved smooth function, many points,
     *   integration inside [a,b]), non-periodic spline
     *
     * * exact test (integration of parabola, outside of [a,b], non-periodic spline
     *
     * * approximate test for periodic splines. F(x)=cos(2*pi*x)+1.
     *   Period length is equals to 1.0, so all operations with
     *   multiples of period are done exactly. For each value of PERIOD
     *   we calculate and test integral at four points:
     *   -   0 < t0 < PERIOD
     *   -   t1 = PERIOD-eps
     *   -   t2 = PERIOD
     *   -   t3 = PERIOD+eps
     */
    err = 0;
    for(n=20; n<=35; n++)
    {
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * Prepare cubic spline
             */
            a = -1-0.2*ae_randomreal(_state);
            b = 1+0.2*ae_randomreal(_state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = a+(b-a)*i/(n-1);
                y.ptr.p_double[i] = ae_sin(ae_pi*x.ptr.p_double[i]+0.4, _state)+ae_exp(x.ptr.p_double[i], _state);
            }
            bl = ae_pi*ae_cos(ae_pi*a+0.4, _state)+ae_exp(a, _state);
            br = ae_pi*ae_cos(ae_pi*b+0.4, _state)+ae_exp(b, _state);
            spline1dbuildcubic(&x, &y, n, 1, bl, 1, br, &c, _state);
            
            /*
             * Test
             */
            t = a+(b-a)*ae_randomreal(_state);
            v = -ae_cos(ae_pi*a+0.4, _state)/ae_pi+ae_exp(a, _state);
            v = -ae_cos(ae_pi*t+0.4, _state)/ae_pi+ae_exp(t, _state)-v;
            v = v-spline1dintegrate(&c, t, _state);
            err = ae_maxreal(err, ae_fabs(v, _state), _state);
        }
    }
    ierrors = ierrors||ae_fp_greater(err,0.001);
    p0 = 2*ae_randomreal(_state)-1;
    p1 = 2*ae_randomreal(_state)-1;
    p2 = 2*ae_randomreal(_state)-1;
    a = -ae_randomreal(_state)-0.5;
    b = ae_randomreal(_state)+0.5;
    n = 2;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    ae_vector_set_length(&d, n, _state);
    x.ptr.p_double[0] = a;
    y.ptr.p_double[0] = p0+p1*a+p2*ae_sqr(a, _state);
    d.ptr.p_double[0] = p1+2*p2*a;
    x.ptr.p_double[1] = b;
    y.ptr.p_double[1] = p0+p1*b+p2*ae_sqr(b, _state);
    d.ptr.p_double[1] = p1+2*p2*b;
    spline1dbuildhermite(&x, &y, &d, n, &c, _state);
    bl = ae_minreal(a, b, _state)-ae_fabs(b-a, _state);
    br = ae_minreal(a, b, _state)+ae_fabs(b-a, _state);
    err = 0;
    for(pass=1; pass<=100; pass++)
    {
        t = bl+(br-bl)*ae_randomreal(_state);
        v = p0*t+p1*ae_sqr(t, _state)/2+p2*ae_sqr(t, _state)*t/3-(p0*a+p1*ae_sqr(a, _state)/2+p2*ae_sqr(a, _state)*a/3);
        v = v-spline1dintegrate(&c, t, _state);
        err = ae_maxreal(err, ae_fabs(v, _state), _state);
    }
    ierrors = ierrors||ae_fp_greater(err,threshold);
    n = 100;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        x.ptr.p_double[i] = (double)i/(double)(n-1);
        y.ptr.p_double[i] = ae_cos(2*ae_pi*x.ptr.p_double[i], _state)+1;
    }
    y.ptr.p_double[0] = 2;
    y.ptr.p_double[n-1] = 2;
    spline1dbuildcubic(&x, &y, n, -1, 0.0, -1, 0.0, &c, _state);
    intab = spline1dintegrate(&c, 1.0, _state);
    v = ae_randomreal(_state);
    vr = spline1dintegrate(&c, v, _state);
    ierrors = ierrors||ae_fp_greater(ae_fabs(intab-1, _state),0.001);
    for(i=-10; i<=10; i++)
    {
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, i+v, _state)-(i*intab+vr), _state),0.001);
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, i-1000*ae_machineepsilon, _state)-i*intab, _state),0.001);
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, i, _state)-i*intab, _state),0.001);
        ierrors = ierrors||ae_fp_greater(ae_fabs(spline1dintegrate(&c, i+1000*ae_machineepsilon, _state)-i*intab, _state),0.001);
    }
    
    /*
     * report
     */
    waserrors = ((((((((lserrors||cserrors)||crserrors)||hserrors)||aserrors)||dserrors)||cperrors)||uperrors)||lterrors)||ierrors;
    if( !silent )
    {
        printf("TESTING SPLINE INTERPOLATION\n");
        
        /*
         * Normal tests
         */
        printf("LINEAR SPLINE TEST:                      ");
        if( lserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CUBIC SPLINE TEST:                       ");
        if( cserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CATMULL-ROM SPLINE TEST:                 ");
        if( crserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("HERMITE SPLINE TEST:                     ");
        if( hserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("AKIMA SPLINE TEST:                       ");
        if( aserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("DIFFERENTIATION TEST:                    ");
        if( dserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("COPY/SERIALIZATION TEST:                 ");
        if( cperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("UNPACK TEST:                             ");
        if( uperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN.TRANS. TEST:                         ");
        if( lterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("INTEGRATION TEST:                        ");
        if( ierrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Lipschitz constants for spline inself, first and second derivatives.
*************************************************************************/
static void testspline1dunit_lconst(double a,
     double b,
     spline1dinterpolant* c,
     double lstep,
     double* l0,
     double* l1,
     double* l2,
     ae_state *_state)
{
    double t;
    double vl;
    double vm;
    double vr;
    double prevf;
    double prevd;
    double prevd2;
    double f;
    double d;
    double d2;

    *l0 = 0;
    *l1 = 0;
    *l2 = 0;

    *l0 = 0;
    *l1 = 0;
    *l2 = 0;
    t = a-0.1;
    vl = spline1dcalc(c, t-2*lstep, _state);
    vm = spline1dcalc(c, t-lstep, _state);
    vr = spline1dcalc(c, t, _state);
    f = vm;
    d = (vr-vl)/(2*lstep);
    d2 = (vr-2*vm+vl)/ae_sqr(lstep, _state);
    while(ae_fp_less_eq(t,b+0.1))
    {
        prevf = f;
        prevd = d;
        prevd2 = d2;
        vl = vm;
        vm = vr;
        vr = spline1dcalc(c, t+lstep, _state);
        f = vm;
        d = (vr-vl)/(2*lstep);
        d2 = (vr-2*vm+vl)/ae_sqr(lstep, _state);
        *l0 = ae_maxreal(*l0, ae_fabs((f-prevf)/lstep, _state), _state);
        *l1 = ae_maxreal(*l1, ae_fabs((d-prevd)/lstep, _state), _state);
        *l2 = ae_maxreal(*l2, ae_fabs((d2-prevd2)/lstep, _state), _state);
        t = t+lstep;
    }
}


/*************************************************************************
Unpack testing
*************************************************************************/
static ae_bool testspline1dunit_testunpack(spline1dinterpolant* c,
     /* Real    */ ae_vector* x,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t n;
    double err;
    double t;
    double v1;
    double v2;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix tbl;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&tbl, 0, 0, DT_REAL, _state, ae_true);

    passcount = 20;
    err = 0;
    spline1dunpack(c, &n, &tbl, _state);
    for(i=0; i<=n-2; i++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            t = ae_randomreal(_state)*(tbl.ptr.pp_double[i][1]-tbl.ptr.pp_double[i][0]);
            v1 = tbl.ptr.pp_double[i][2]+t*tbl.ptr.pp_double[i][3]+ae_sqr(t, _state)*tbl.ptr.pp_double[i][4]+t*ae_sqr(t, _state)*tbl.ptr.pp_double[i][5];
            v2 = spline1dcalc(c, tbl.ptr.pp_double[i][0]+t, _state);
            err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
        }
    }
    for(i=0; i<=n-2; i++)
    {
        err = ae_maxreal(err, ae_fabs(x->ptr.p_double[i]-tbl.ptr.pp_double[i][0], _state), _state);
    }
    for(i=0; i<=n-2; i++)
    {
        err = ae_maxreal(err, ae_fabs(x->ptr.p_double[i+1]-tbl.ptr.pp_double[i][1], _state), _state);
    }
    result = ae_fp_less(err,100*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testspline1dunit_unsetspline1d(spline1dinterpolant* c,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_vector d;

    ae_frame_make(_state, &_frame_block);
    _spline1dinterpolant_clear(c);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&y, 2, _state);
    ae_vector_set_length(&d, 2, _state);
    x.ptr.p_double[0] = -1;
    y.ptr.p_double[0] = ae_randomreal(_state);
    d.ptr.p_double[0] = ae_randomreal(_state);
    x.ptr.p_double[1] = 1;
    y.ptr.p_double[1] = ae_randomreal(_state);
    d.ptr.p_double[1] = ae_randomreal(_state);
    spline1dbuildhermite(&x, &y, &d, 2, c, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testspline1dunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}


/*************************************************************************
Tests whether constant C is solution of 1D LLS problem
*************************************************************************/
static ae_bool testspline1dunit_is1dsolution(ae_int_t n,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     double c,
     ae_state *_state)
{
    ae_int_t i;
    double s1;
    double s2;
    double s3;
    double delta;
    ae_bool result;


    delta = 0.001;
    
    /*
     * Test result
     */
    s1 = 0;
    for(i=0; i<=n-1; i++)
    {
        s1 = s1+ae_sqr(w->ptr.p_double[i]*(c-y->ptr.p_double[i]), _state);
    }
    s2 = 0;
    s3 = 0;
    for(i=0; i<=n-1; i++)
    {
        s2 = s2+ae_sqr(w->ptr.p_double[i]*(c+delta-y->ptr.p_double[i]), _state);
        s3 = s3+ae_sqr(w->ptr.p_double[i]*(c-delta-y->ptr.p_double[i]), _state);
    }
    result = ae_fp_greater_eq(s2,s1)&&ae_fp_greater_eq(s3,s1);
    return result;
}



static ae_bool testminlmunit_rkindvsstatecheck(ae_int_t rkind,
     minlmstate* state,
     ae_state *_state);
static void testminlmunit_axmb(minlmstate* state,
     /* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     ae_state *_state);





ae_bool testminlm(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool referror;
    ae_bool lin1error;
    ae_bool lin2error;
    ae_bool eqerror;
    ae_bool converror;
    ae_bool scerror;
    ae_bool restartserror;
    ae_bool othererrors;
    ae_int_t rkind;
    ae_int_t ckind;
    ae_int_t tmpkind;
    double epsf;
    double epsx;
    double epsg;
    ae_int_t maxits;
    ae_int_t n;
    ae_int_t m;
    ae_vector x;
    ae_vector xe;
    ae_vector b;
    ae_vector bl;
    ae_vector bu;
    ae_vector xlast;
    ae_int_t i;
    ae_int_t j;
    double v;
    double s;
    double stpmax;
    double h;
    ae_matrix a;
    double fprev;
    double xprev;
    minlmstate state;
    minlmreport rep;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xe, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    _minlmstate_init(&state, _state, ae_true);
    _minlmreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    referror = ae_false;
    lin1error = ae_false;
    lin2error = ae_false;
    eqerror = ae_false;
    converror = ae_false;
    scerror = ae_false;
    othererrors = ae_false;
    restartserror = ae_false;
    
    /*
     * Reference problem.
     * See comments for RKindVsStateCheck() for more info about RKind.
     *
     * NOTES: we also test negative RKind's corresponding to "inexact" schemes
     * which use approximate finite difference Jacobian.
     */
    ae_vector_set_length(&x, 3, _state);
    n = 3;
    m = 3;
    h = 0.0001;
    for(rkind=-2; rkind<=5; rkind++)
    {
        x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[1] = 100*ae_randomreal(_state)-50;
        x.ptr.p_double[2] = 100*ae_randomreal(_state)-50;
        if( rkind==-2 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==-1 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==0 )
        {
            minlmcreatefj(n, m, &x, &state, _state);
        }
        if( rkind==1 )
        {
            minlmcreatefgj(n, m, &x, &state, _state);
        }
        if( rkind==2 )
        {
            minlmcreatefgh(n, &x, &state, _state);
        }
        if( rkind==3 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==4 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==5 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 2, _state);
        }
        while(minlmiteration(&state, _state))
        {
            
            /*
             * (x-2)^2 + y^2 + (z-x)^2
             */
            if( state.needfi )
            {
                state.fi.ptr.p_double[0] = state.x.ptr.p_double[0]-2;
                state.fi.ptr.p_double[1] = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
            }
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = state.x.ptr.p_double[0]-2;
                state.fi.ptr.p_double[1] = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
                state.j.ptr.pp_double[0][0] = 1;
                state.j.ptr.pp_double[0][1] = 0;
                state.j.ptr.pp_double[0][2] = 0;
                state.j.ptr.pp_double[1][0] = 0;
                state.j.ptr.pp_double[1][1] = 1;
                state.j.ptr.pp_double[1][2] = 0;
                state.j.ptr.pp_double[2][0] = -1;
                state.j.ptr.pp_double[2][1] = 0;
                state.j.ptr.pp_double[2][2] = 1;
            }
            if( (state.needf||state.needfg)||state.needfgh )
            {
                state.f = ae_sqr(state.x.ptr.p_double[0]-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
            }
            if( state.needfg||state.needfgh )
            {
                state.g.ptr.p_double[0] = 2*(state.x.ptr.p_double[0]-2)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
            }
            if( state.needfgh )
            {
                state.h.ptr.pp_double[0][0] = 4;
                state.h.ptr.pp_double[0][1] = 0;
                state.h.ptr.pp_double[0][2] = -2;
                state.h.ptr.pp_double[1][0] = 0;
                state.h.ptr.pp_double[1][1] = 2;
                state.h.ptr.pp_double[1][2] = 0;
                state.h.ptr.pp_double[2][0] = -2;
                state.h.ptr.pp_double[2][1] = 0;
                state.h.ptr.pp_double[2][2] = 2;
            }
            scerror = scerror||!testminlmunit_rkindvsstatecheck(rkind, &state, _state);
        }
        minlmresults(&state, &x, &rep, _state);
        referror = (((referror||rep.terminationtype<=0)||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-2, _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.001))||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-2, _state),0.001);
    }
    
    /*
     * Reference bound constrained problem:
     *
     *     min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1
     *
     * NOTES:
     * 1. we test only two optimization modes - V and FGH,
     *    because from algorithm internals we can assume that actual
     *    mode being used doesn't matter for bound constrained optimization
     *    process.
     */
    for(tmpkind=0; tmpkind<=1; tmpkind++)
    {
        for(n=1; n<=5; n++)
        {
            ae_vector_set_length(&bl, n, _state);
            ae_vector_set_length(&bu, n, _state);
            ae_vector_set_length(&xe, n, _state);
            ae_vector_set_length(&x, n, _state);
            for(i=0; i<=n-1; i++)
            {
                bl.ptr.p_double[i] = 0;
                bu.ptr.p_double[i] = 1;
                xe.ptr.p_double[i] = 3*ae_randomreal(_state)-1;
                x.ptr.p_double[i] = ae_randomreal(_state);
            }
            if( tmpkind==0 )
            {
                minlmcreatefgh(n, &x, &state, _state);
            }
            if( tmpkind==1 )
            {
                minlmcreatev(n, n, &x, 1.0E-3, &state, _state);
            }
            minlmsetcond(&state, 1.0E-6, 0, 0, 0, _state);
            minlmsetbc(&state, &bl, &bu, _state);
            while(minlmiteration(&state, _state))
            {
                if( state.needfi )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        state.fi.ptr.p_double[i] = ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], 2, _state);
                    }
                }
                if( (state.needf||state.needfg)||state.needfgh )
                {
                    state.f = 0;
                    for(i=0; i<=n-1; i++)
                    {
                        state.f = state.f+ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], 4, _state);
                    }
                }
                if( state.needfg||state.needfgh )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        state.g.ptr.p_double[i] = 4*ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], 3, _state);
                    }
                }
                if( state.needfgh )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            state.h.ptr.pp_double[i][j] = 0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        state.h.ptr.pp_double[i][i] = 12*ae_pow(state.x.ptr.p_double[i]-xe.ptr.p_double[i], 2, _state);
                    }
                }
            }
            minlmresults(&state, &x, &rep, _state);
            if( rep.terminationtype==4 )
            {
                for(i=0; i<=n-1; i++)
                {
                    referror = referror||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-boundval(xe.ptr.p_double[i], bl.ptr.p_double[i], bu.ptr.p_double[i], _state), _state),5.0E-2);
                }
            }
            else
            {
                referror = ae_true;
            }
        }
    }
    
    /*
     * 1D problem #1
     *
     * NOTES: we also test negative RKind's corresponding to "inexact" schemes
     * which use approximate finite difference Jacobian.
     */
    for(rkind=-2; rkind<=5; rkind++)
    {
        ae_vector_set_length(&x, 1, _state);
        n = 1;
        m = 1;
        h = 0.00001;
        x.ptr.p_double[0] = 100*ae_randomreal(_state)-50;
        if( rkind==-2 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==-1 )
        {
            minlmcreatev(n, m, &x, h, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==0 )
        {
            minlmcreatefj(n, m, &x, &state, _state);
        }
        if( rkind==1 )
        {
            minlmcreatefgj(n, m, &x, &state, _state);
        }
        if( rkind==2 )
        {
            minlmcreatefgh(n, &x, &state, _state);
        }
        if( rkind==3 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==4 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==5 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 2, _state);
        }
        while(minlmiteration(&state, _state))
        {
            if( state.needfi )
            {
                state.fi.ptr.p_double[0] = ae_sin(state.x.ptr.p_double[0], _state);
            }
            if( state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sin(state.x.ptr.p_double[0], _state);
                state.j.ptr.pp_double[0][0] = ae_cos(state.x.ptr.p_double[0], _state);
            }
            if( (state.needf||state.needfg)||state.needfgh )
            {
                state.f = ae_sqr(ae_sin(state.x.ptr.p_double[0], _state), _state);
            }
            if( state.needfg||state.needfgh )
            {
                state.g.ptr.p_double[0] = 2*ae_sin(state.x.ptr.p_double[0], _state)*ae_cos(state.x.ptr.p_double[0], _state);
            }
            if( state.needfgh )
            {
                state.h.ptr.pp_double[0][0] = 2*(ae_cos(state.x.ptr.p_double[0], _state)*ae_cos(state.x.ptr.p_double[0], _state)-ae_sin(state.x.ptr.p_double[0], _state)*ae_sin(state.x.ptr.p_double[0], _state));
            }
            scerror = scerror||!testminlmunit_rkindvsstatecheck(rkind, &state, _state);
        }
        minlmresults(&state, &x, &rep, _state);
        lin1error = rep.terminationtype<=0||ae_fp_greater(ae_fabs(x.ptr.p_double[0]/ae_pi-ae_round(x.ptr.p_double[0]/ae_pi, _state), _state),0.001);
    }
    
    /*
     * Linear equations: test normal optimization and optimization with restarts
     */
    for(n=1; n<=10; n++)
    {
        
        /*
         * Prepare task
         */
        h = 0.00001;
        rmatrixrndcond(n, 100, &a, _state);
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xe, n, _state);
        ae_vector_set_length(&b, n, _state);
        for(i=0; i<=n-1; i++)
        {
            xe.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
        }
        for(i=0; i<=n-1; i++)
        {
            v = ae_v_dotproduct(&a.ptr.pp_double[i][0], 1, &xe.ptr.p_double[0], 1, ae_v_len(0,n-1));
            b.ptr.p_double[i] = v;
        }
        
        /*
         * Test different RKind
         *
         * NOTES: we also test negative RKind's corresponding to "inexact" schemes
         * which use approximate finite difference Jacobian.
         */
        for(rkind=-2; rkind<=5; rkind++)
        {
            
            /*
             * Solve task (first attempt)
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            if( rkind==-2 )
            {
                minlmcreatev(n, n, &x, h, &state, _state);
                minlmsetacctype(&state, 1, _state);
            }
            if( rkind==-1 )
            {
                minlmcreatev(n, n, &x, h, &state, _state);
                minlmsetacctype(&state, 0, _state);
            }
            if( rkind==0 )
            {
                minlmcreatefj(n, n, &x, &state, _state);
            }
            if( rkind==1 )
            {
                minlmcreatefgj(n, n, &x, &state, _state);
            }
            if( rkind==2 )
            {
                minlmcreatefgh(n, &x, &state, _state);
            }
            if( rkind==3 )
            {
                minlmcreatevj(n, n, &x, &state, _state);
                minlmsetacctype(&state, 0, _state);
            }
            if( rkind==4 )
            {
                minlmcreatevj(n, n, &x, &state, _state);
                minlmsetacctype(&state, 1, _state);
            }
            if( rkind==5 )
            {
                minlmcreatevj(n, n, &x, &state, _state);
                minlmsetacctype(&state, 2, _state);
            }
            while(minlmiteration(&state, _state))
            {
                testminlmunit_axmb(&state, &a, &b, n, _state);
                scerror = scerror||!testminlmunit_rkindvsstatecheck(rkind, &state, _state);
            }
            minlmresults(&state, &x, &rep, _state);
            eqerror = eqerror||rep.terminationtype<=0;
            for(i=0; i<=n-1; i++)
            {
                eqerror = eqerror||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001);
            }
            
            /*
             * Now we try to restart algorithm from new point
             */
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
            }
            minlmrestartfrom(&state, &x, _state);
            while(minlmiteration(&state, _state))
            {
                testminlmunit_axmb(&state, &a, &b, n, _state);
                scerror = scerror||!testminlmunit_rkindvsstatecheck(rkind, &state, _state);
            }
            minlmresults(&state, &x, &rep, _state);
            restartserror = restartserror||rep.terminationtype<=0;
            for(i=0; i<=n-1; i++)
            {
                restartserror = restartserror||ae_fp_greater(ae_fabs(x.ptr.p_double[i]-xe.ptr.p_double[i], _state),0.001);
            }
        }
    }
    
    /*
     * Testing convergence properties using
     * different optimizer types and different conditions.
     *
     * Only limited subset of optimizers is tested because some
     * optimizers converge too quickly.
     */
    s = 100;
    for(rkind=0; rkind<=5; rkind++)
    {
        
        /*
         * Skip FGH optimizer - it converges too quickly
         */
        if( rkind==2 )
        {
            continue;
        }
        
        /*
         * Test
         */
        for(ckind=0; ckind<=3; ckind++)
        {
            epsg = 0;
            epsf = 0;
            epsx = 0;
            maxits = 0;
            if( ckind==0 )
            {
                epsf = 0.000001;
            }
            if( ckind==1 )
            {
                epsx = 0.000001;
            }
            if( ckind==2 )
            {
                maxits = 2;
            }
            if( ckind==3 )
            {
                epsg = 0.0001;
            }
            ae_vector_set_length(&x, 3, _state);
            n = 3;
            m = 3;
            for(i=0; i<=2; i++)
            {
                x.ptr.p_double[i] = 6;
            }
            if( rkind==0 )
            {
                minlmcreatefj(n, m, &x, &state, _state);
            }
            if( rkind==1 )
            {
                minlmcreatefgj(n, m, &x, &state, _state);
            }
            ae_assert(rkind!=2, "Assertion failed", _state);
            if( rkind==3 )
            {
                minlmcreatevj(n, m, &x, &state, _state);
                minlmsetacctype(&state, 0, _state);
            }
            if( rkind==4 )
            {
                minlmcreatevj(n, m, &x, &state, _state);
                minlmsetacctype(&state, 1, _state);
            }
            if( rkind==5 )
            {
                minlmcreatevj(n, m, &x, &state, _state);
                minlmsetacctype(&state, 2, _state);
            }
            minlmsetcond(&state, epsg, epsf, epsx, maxits, _state);
            while(minlmiteration(&state, _state))
            {
                if( state.needfi||state.needfij )
                {
                    state.fi.ptr.p_double[0] = s*(ae_exp(state.x.ptr.p_double[0], _state)-2);
                    state.fi.ptr.p_double[1] = ae_sqr(state.x.ptr.p_double[1], _state)+1;
                    state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
                }
                if( state.needfij )
                {
                    state.j.ptr.pp_double[0][0] = s*ae_exp(state.x.ptr.p_double[0], _state);
                    state.j.ptr.pp_double[0][1] = 0;
                    state.j.ptr.pp_double[0][2] = 0;
                    state.j.ptr.pp_double[1][0] = 0;
                    state.j.ptr.pp_double[1][1] = 2*state.x.ptr.p_double[1];
                    state.j.ptr.pp_double[1][2] = 0;
                    state.j.ptr.pp_double[2][0] = -1;
                    state.j.ptr.pp_double[2][1] = 0;
                    state.j.ptr.pp_double[2][2] = 1;
                }
                if( (state.needf||state.needfg)||state.needfgh )
                {
                    state.f = s*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(ae_sqr(state.x.ptr.p_double[1], _state)+1, _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
                }
                if( state.needfg||state.needfgh )
                {
                    state.g.ptr.p_double[0] = s*2*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                    state.g.ptr.p_double[1] = 2*(ae_sqr(state.x.ptr.p_double[1], _state)+1)*2*state.x.ptr.p_double[1];
                    state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
                }
                if( state.needfgh )
                {
                    state.h.ptr.pp_double[0][0] = s*(4*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state), _state)-4*ae_exp(state.x.ptr.p_double[0], _state))+2;
                    state.h.ptr.pp_double[0][1] = 0;
                    state.h.ptr.pp_double[0][2] = -2;
                    state.h.ptr.pp_double[1][0] = 0;
                    state.h.ptr.pp_double[1][1] = 12*ae_sqr(state.x.ptr.p_double[1], _state)+4;
                    state.h.ptr.pp_double[1][2] = 0;
                    state.h.ptr.pp_double[2][0] = -2;
                    state.h.ptr.pp_double[2][1] = 0;
                    state.h.ptr.pp_double[2][2] = 2;
                }
                scerror = scerror||!testminlmunit_rkindvsstatecheck(rkind, &state, _state);
            }
            minlmresults(&state, &x, &rep, _state);
            if( ckind==0 )
            {
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log(2, _state), _state),0.05);
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.05);
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log(2, _state), _state),0.05);
                converror = converror||rep.terminationtype!=1;
            }
            if( ckind==1 )
            {
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log(2, _state), _state),0.05);
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.05);
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log(2, _state), _state),0.05);
                converror = converror||rep.terminationtype!=2;
            }
            if( ckind==2 )
            {
                converror = (converror||rep.terminationtype!=5)||rep.iterationscount!=maxits;
            }
            if( ckind==3 )
            {
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[0]-ae_log(2, _state), _state),0.05);
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[1], _state),0.05);
                converror = converror||ae_fp_greater(ae_fabs(x.ptr.p_double[2]-ae_log(2, _state), _state),0.05);
                converror = converror||rep.terminationtype!=4;
            }
        }
    }
    
    /*
     * Other properties:
     * 1. test reports (F should form monotone sequence)
     * 2. test maximum step
     */
    for(rkind=0; rkind<=5; rkind++)
    {
        
        /*
         * reports:
         * * check that first report is initial point
         * * check that F is monotone decreasing
         * * check that last report is final result
         */
        n = 3;
        m = 3;
        s = 100;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&xlast, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 6;
        }
        if( rkind==0 )
        {
            minlmcreatefj(n, m, &x, &state, _state);
        }
        if( rkind==1 )
        {
            minlmcreatefgj(n, m, &x, &state, _state);
        }
        if( rkind==2 )
        {
            minlmcreatefgh(n, &x, &state, _state);
        }
        if( rkind==3 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 0, _state);
        }
        if( rkind==4 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 1, _state);
        }
        if( rkind==5 )
        {
            minlmcreatevj(n, m, &x, &state, _state);
            minlmsetacctype(&state, 2, _state);
        }
        minlmsetcond(&state, 0, 0, 0, 4, _state);
        minlmsetxrep(&state, ae_true, _state);
        fprev = ae_maxrealnumber;
        while(minlmiteration(&state, _state))
        {
            if( state.needfi||state.needfij )
            {
                state.fi.ptr.p_double[0] = ae_sqrt(s, _state)*(ae_exp(state.x.ptr.p_double[0], _state)-2);
                state.fi.ptr.p_double[1] = state.x.ptr.p_double[1];
                state.fi.ptr.p_double[2] = state.x.ptr.p_double[2]-state.x.ptr.p_double[0];
            }
            if( state.needfij )
            {
                state.j.ptr.pp_double[0][0] = ae_sqrt(s, _state)*ae_exp(state.x.ptr.p_double[0], _state);
                state.j.ptr.pp_double[0][1] = 0;
                state.j.ptr.pp_double[0][2] = 0;
                state.j.ptr.pp_double[1][0] = 0;
                state.j.ptr.pp_double[1][1] = 1;
                state.j.ptr.pp_double[1][2] = 0;
                state.j.ptr.pp_double[2][0] = -1;
                state.j.ptr.pp_double[2][1] = 0;
                state.j.ptr.pp_double[2][2] = 1;
            }
            if( (state.needf||state.needfg)||state.needfgh )
            {
                state.f = s*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state)-2, _state)+ae_sqr(state.x.ptr.p_double[1], _state)+ae_sqr(state.x.ptr.p_double[2]-state.x.ptr.p_double[0], _state);
            }
            if( state.needfg||state.needfgh )
            {
                state.g.ptr.p_double[0] = s*2*(ae_exp(state.x.ptr.p_double[0], _state)-2)*ae_exp(state.x.ptr.p_double[0], _state)+2*(state.x.ptr.p_double[0]-state.x.ptr.p_double[2]);
                state.g.ptr.p_double[1] = 2*state.x.ptr.p_double[1];
                state.g.ptr.p_double[2] = 2*(state.x.ptr.p_double[2]-state.x.ptr.p_double[0]);
            }
            if( state.needfgh )
            {
                state.h.ptr.pp_double[0][0] = s*(4*ae_sqr(ae_exp(state.x.ptr.p_double[0], _state), _state)-4*ae_exp(state.x.ptr.p_double[0], _state))+2;
                state.h.ptr.pp_double[0][1] = 0;
                state.h.ptr.pp_double[0][2] = -2;
                state.h.ptr.pp_double[1][0] = 0;
                state.h.ptr.pp_double[1][1] = 2;
                state.h.ptr.pp_double[1][2] = 0;
                state.h.ptr.pp_double[2][0] = -2;
                state.h.ptr.pp_double[2][1] = 0;
                state.h.ptr.pp_double[2][2] = 2;
            }
            scerror = scerror||!testminlmunit_rkindvsstatecheck(rkind, &state, _state);
            if( state.xupdated )
            {
                othererrors = othererrors||ae_fp_greater(state.f,fprev);
                if( ae_fp_eq(fprev,ae_maxrealnumber) )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        othererrors = othererrors||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                    }
                }
                fprev = state.f;
                ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
        }
        minlmresults(&state, &x, &rep, _state);
        for(i=0; i<=n-1; i++)
        {
            othererrors = othererrors||ae_fp_neq(x.ptr.p_double[i],xlast.ptr.p_double[i]);
        }
    }
    n = 1;
    ae_vector_set_length(&x, n, _state);
    x.ptr.p_double[0] = 100;
    stpmax = 0.05+0.05*ae_randomreal(_state);
    minlmcreatefgh(n, &x, &state, _state);
    minlmsetcond(&state, 1.0E-9, 0, 0, 0, _state);
    minlmsetstpmax(&state, stpmax, _state);
    minlmsetxrep(&state, ae_true, _state);
    xprev = x.ptr.p_double[0];
    while(minlmiteration(&state, _state))
    {
        if( (state.needf||state.needfg)||state.needfgh )
        {
            state.f = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
        }
        if( state.needfg||state.needfgh )
        {
            state.g.ptr.p_double[0] = ae_exp(state.x.ptr.p_double[0], _state)-ae_exp(-state.x.ptr.p_double[0], _state);
        }
        if( state.needfgh )
        {
            state.h.ptr.pp_double[0][0] = ae_exp(state.x.ptr.p_double[0], _state)+ae_exp(-state.x.ptr.p_double[0], _state);
        }
        othererrors = othererrors||ae_fp_greater(ae_fabs(state.x.ptr.p_double[0]-xprev, _state),(1+ae_sqrt(ae_machineepsilon, _state))*stpmax);
        if( state.xupdated )
        {
            xprev = state.x.ptr.p_double[0];
        }
    }
    
    /*
     * end
     */
    waserrors = ((((((referror||lin1error)||lin2error)||eqerror)||converror)||scerror)||othererrors)||restartserror;
    if( !silent )
    {
        printf("TESTING LEVENBERG-MARQUARDT OPTIMIZATION\n");
        printf("REFERENCE PROBLEMS:                       ");
        if( referror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("1-D PROBLEM #1:                           ");
        if( lin1error )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("1-D PROBLEM #2:                           ");
        if( lin2error )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LINEAR EQUATIONS:                         ");
        if( eqerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RESTARTS:                                 ");
        if( restartserror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE PROPERTIES:                   ");
        if( converror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("STATE FIELDS CONSISTENCY:                 ");
        if( scerror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Asserts that State fields are consistent with RKind.
Returns False otherwise.

RKind is an algorithm selector:
* -2 = V, AccType=1
* -1 = V, AccType=0
*  0 = FJ
*  1 = FGJ
*  2 = FGH
*  3 = VJ, AccType=0
*  4 = VJ, AccType=1
*  5 = VJ, AccType=2

*************************************************************************/
static ae_bool testminlmunit_rkindvsstatecheck(ae_int_t rkind,
     minlmstate* state,
     ae_state *_state)
{
    ae_int_t nset;
    ae_bool result;


    nset = 0;
    if( state->needfi )
    {
        nset = nset+1;
    }
    if( state->needf )
    {
        nset = nset+1;
    }
    if( state->needfg )
    {
        nset = nset+1;
    }
    if( state->needfij )
    {
        nset = nset+1;
    }
    if( state->needfgh )
    {
        nset = nset+1;
    }
    if( state->xupdated )
    {
        nset = nset+1;
    }
    if( nset!=1 )
    {
        result = ae_false;
        return result;
    }
    if( rkind==-2 )
    {
        result = state->needfi||state->xupdated;
        return result;
    }
    if( rkind==-1 )
    {
        result = state->needfi||state->xupdated;
        return result;
    }
    if( rkind==0 )
    {
        result = (state->needf||state->needfij)||state->xupdated;
        return result;
    }
    if( rkind==1 )
    {
        result = ((state->needf||state->needfij)||state->needfg)||state->xupdated;
        return result;
    }
    if( rkind==2 )
    {
        result = ((state->needf||state->needfg)||state->needfgh)||state->xupdated;
        return result;
    }
    if( rkind==3 )
    {
        result = (state->needfi||state->needfij)||state->xupdated;
        return result;
    }
    if( rkind==4 )
    {
        result = (state->needfi||state->needfij)||state->xupdated;
        return result;
    }
    if( rkind==5 )
    {
        result = (state->needfi||state->needfij)||state->xupdated;
        return result;
    }
    result = ae_false;
    return result;
}


/*************************************************************************
Calculates FI/F/G/H for problem min(||Ax-b||)
*************************************************************************/
static void testminlmunit_axmb(minlmstate* state,
     /* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* b,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;


    if( (state->needf||state->needfg)||state->needfgh )
    {
        state->f = 0;
    }
    if( state->needfg||state->needfgh )
    {
        for(i=0; i<=n-1; i++)
        {
            state->g.ptr.p_double[i] = 0;
        }
    }
    if( state->needfgh )
    {
        for(i=0; i<=n-1; i++)
        {
            for(j=0; j<=n-1; j++)
            {
                state->h.ptr.pp_double[i][j] = 0;
            }
        }
    }
    for(i=0; i<=n-1; i++)
    {
        v = ae_v_dotproduct(&a->ptr.pp_double[i][0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        if( (state->needf||state->needfg)||state->needfgh )
        {
            state->f = state->f+ae_sqr(v-b->ptr.p_double[i], _state);
        }
        if( state->needfg||state->needfgh )
        {
            for(j=0; j<=n-1; j++)
            {
                state->g.ptr.p_double[j] = state->g.ptr.p_double[j]+2*(v-b->ptr.p_double[i])*a->ptr.pp_double[i][j];
            }
        }
        if( state->needfgh )
        {
            for(j=0; j<=n-1; j++)
            {
                for(k=0; k<=n-1; k++)
                {
                    state->h.ptr.pp_double[j][k] = state->h.ptr.pp_double[j][k]+2*a->ptr.pp_double[i][j]*a->ptr.pp_double[i][k];
                }
            }
        }
        if( state->needfi )
        {
            state->fi.ptr.p_double[i] = v-b->ptr.p_double[i];
        }
        if( state->needfij )
        {
            state->fi.ptr.p_double[i] = v-b->ptr.p_double[i];
            ae_v_move(&state->j.ptr.pp_double[i][0], 1, &a->ptr.pp_double[i][0], 1, ae_v_len(0,n-1));
        }
    }
}



static void testlsfitunit_testpolynomialfitting(ae_bool* fiterrors,
     ae_state *_state);
static void testlsfitunit_testrationalfitting(ae_bool* fiterrors,
     ae_state *_state);
static void testlsfitunit_testsplinefitting(ae_bool* fiterrors,
     ae_state *_state);
static void testlsfitunit_testgeneralfitting(ae_bool* llserrors,
     ae_bool* nlserrors,
     ae_state *_state);
static ae_bool testlsfitunit_isglssolution(ae_int_t n,
     ae_int_t m,
     ae_int_t k,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_matrix* cmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state);
static double testlsfitunit_getglserror(ae_int_t n,
     ae_int_t m,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state);
static void testlsfitunit_fitlinearnonlinear(ae_int_t m,
     ae_int_t deravailable,
     /* Real    */ ae_matrix* xy,
     lsfitstate* state,
     ae_bool* nlserrors,
     ae_state *_state);





ae_bool testlsfit(ae_bool silent, ae_state *_state)
{
    ae_bool waserrors;
    ae_bool llserrors;
    ae_bool nlserrors;
    ae_bool polfiterrors;
    ae_bool ratfiterrors;
    ae_bool splfiterrors;
    ae_bool result;


    waserrors = ae_false;
    testlsfitunit_testpolynomialfitting(&polfiterrors, _state);
    testlsfitunit_testrationalfitting(&ratfiterrors, _state);
    testlsfitunit_testsplinefitting(&splfiterrors, _state);
    testlsfitunit_testgeneralfitting(&llserrors, &nlserrors, _state);
    
    /*
     * report
     */
    waserrors = (((llserrors||nlserrors)||polfiterrors)||ratfiterrors)||splfiterrors;
    if( !silent )
    {
        printf("TESTING LEAST SQUARES\n");
        printf("POLYNOMIAL LEAST SQUARES:                ");
        if( polfiterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("RATIONAL LEAST SQUARES:                  ");
        if( ratfiterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("SPLINE LEAST SQUARES:                    ");
        if( splfiterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LINEAR LEAST SQUARES:                    ");
        if( llserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("NON-LINEAR LEAST SQUARES:                ");
        if( nlserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    return result;
}


/*************************************************************************
Unit test
*************************************************************************/
static void testlsfitunit_testpolynomialfitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector x2;
    ae_vector y2;
    ae_vector w2;
    ae_vector xfull;
    ae_vector yfull;
    double t;
    ae_int_t i;
    ae_int_t k;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    ae_int_t info;
    ae_int_t info2;
    double v;
    double v0;
    double v1;
    double v2;
    double s;
    double xmin;
    double xmax;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    barycentricinterpolant p;
    barycentricinterpolant p1;
    barycentricinterpolant p2;
    polynomialfitreport rep;
    polynomialfitreport rep2;
    ae_int_t n;
    ae_int_t m;
    ae_int_t maxn;
    ae_int_t pass;
    ae_int_t passcount;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yfull, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _barycentricinterpolant_init(&p, _state, ae_true);
    _barycentricinterpolant_init(&p1, _state, ae_true);
    _barycentricinterpolant_init(&p2, _state, ae_true);
    _polynomialfitreport_init(&rep, _state, ae_true);
    _polynomialfitreport_init(&rep2, _state, ae_true);

    *fiterrors = ae_false;
    maxn = 5;
    passcount = 20;
    threshold = 1.0E8*ae_machineepsilon;
    
    /*
     * Test polunomial fitting
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            
            /*
             * N=M+K fitting (i.e. interpolation)
             */
            for(k=0; k<=n-1; k++)
            {
                taskgenint1d(-1, 1, n, &xfull, &yfull, _state);
                ae_vector_set_length(&x, n-k, _state);
                ae_vector_set_length(&y, n-k, _state);
                ae_vector_set_length(&w, n-k, _state);
                if( k>0 )
                {
                    ae_vector_set_length(&xc, k, _state);
                    ae_vector_set_length(&yc, k, _state);
                    ae_vector_set_length(&dc, k, _state);
                }
                for(i=0; i<=n-k-1; i++)
                {
                    x.ptr.p_double[i] = xfull.ptr.p_double[i];
                    y.ptr.p_double[i] = yfull.ptr.p_double[i];
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                for(i=0; i<=k-1; i++)
                {
                    xc.ptr.p_double[i] = xfull.ptr.p_double[n-k+i];
                    yc.ptr.p_double[i] = yfull.ptr.p_double[n-k+i];
                    dc.ptr.p_int[i] = 0;
                }
                polynomialfitwc(&x, &y, &w, n-k, &xc, &yc, &dc, k, n, &info, &p1, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&p1, x.ptr.p_double[i], _state)-y.ptr.p_double[i], _state),threshold);
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&p1, xc.ptr.p_double[i], _state)-yc.ptr.p_double[i], _state),threshold);
                    }
                }
            }
            
            /*
             * Testing constraints on derivatives.
             * Special tasks which will always have solution:
             * 1. P(0)=YC[0]
             * 2. P(0)=YC[0], P'(0)=YC[1]
             */
            if( n>1 )
            {
                for(m=3; m<=5; m++)
                {
                    for(k=1; k<=2; k++)
                    {
                        taskgenint1d(-1, 1, n, &x, &y, _state);
                        ae_vector_set_length(&w, n, _state);
                        ae_vector_set_length(&xc, 2, _state);
                        ae_vector_set_length(&yc, 2, _state);
                        ae_vector_set_length(&dc, 2, _state);
                        for(i=0; i<=n-1; i++)
                        {
                            w.ptr.p_double[i] = 1+ae_randomreal(_state);
                        }
                        xc.ptr.p_double[0] = 0;
                        yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                        dc.ptr.p_int[0] = 0;
                        xc.ptr.p_double[1] = 0;
                        yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                        dc.ptr.p_int[1] = 1;
                        polynomialfitwc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &p1, &rep, _state);
                        if( info<=0 )
                        {
                            *fiterrors = ae_true;
                        }
                        else
                        {
                            barycentricdiff1(&p1, 0.0, &v0, &v1, _state);
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v0-yc.ptr.p_double[0], _state),threshold);
                            if( k==2 )
                            {
                                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-yc.ptr.p_double[1], _state),threshold);
                            }
                        }
                    }
                }
            }
        }
    }
    for(m=2; m<=8; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * General fitting
             *
             * interpolating function through M nodes should have
             * greater RMS error than fitting it through the same M nodes
             */
            n = 100;
            ae_vector_set_length(&x2, n, _state);
            ae_vector_set_length(&y2, n, _state);
            ae_vector_set_length(&w2, n, _state);
            xmin = 0;
            xmax = 2*ae_pi;
            for(i=0; i<=n-1; i++)
            {
                x2.ptr.p_double[i] = 2*ae_pi*ae_randomreal(_state);
                y2.ptr.p_double[i] = ae_sin(x2.ptr.p_double[i], _state);
                w2.ptr.p_double[i] = 1;
            }
            ae_vector_set_length(&x, m, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                x.ptr.p_double[i] = xmin+(xmax-xmin)*i/(m-1);
                y.ptr.p_double[i] = ae_sin(x.ptr.p_double[i], _state);
            }
            polynomialbuild(&x, &y, m, &p1, _state);
            polynomialfitwc(&x2, &y2, &w2, n, &xc, &yc, &dc, 0, m, &info, &p2, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate P1 (interpolant) RMS error, compare with P2 error
                 */
                v1 = 0;
                v2 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v1 = v1+ae_sqr(barycentriccalc(&p1, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                    v2 = v2+ae_sqr(barycentriccalc(&p2, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                }
                v1 = ae_sqrt(v1/n, _state);
                v2 = ae_sqrt(v2/n, _state);
                *fiterrors = *fiterrors||ae_fp_greater(v2,v1);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v2-rep.rmserror, _state),threshold);
            }
            
            /*
             * compare weighted and non-weighted
             */
            n = 20;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = 1;
            }
            polynomialfitwc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info, &p1, &rep, _state);
            polynomialfit(&x, &y, n, m, &info2, &p2, &rep2, _state);
            if( info<=0||info2<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate P1 (interpolant), compare with P2 error
                 * compare RMS errors
                 */
                t = 2*ae_randomreal(_state)-1;
                v1 = barycentriccalc(&p1, t, _state);
                v2 = barycentriccalc(&p2, t, _state);
                *fiterrors = *fiterrors||ae_fp_neq(v2,v1);
                *fiterrors = *fiterrors||ae_fp_neq(rep.rmserror,rep2.rmserror);
                *fiterrors = *fiterrors||ae_fp_neq(rep.avgerror,rep2.avgerror);
                *fiterrors = *fiterrors||ae_fp_neq(rep.avgrelerror,rep2.avgrelerror);
                *fiterrors = *fiterrors||ae_fp_neq(rep.maxerror,rep2.maxerror);
            }
        }
    }
    for(m=1; m<=maxn; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            ae_assert(passcount>=2, "PassCount should be 2 or greater!", _state);
            
            /*
             * solve simple task (all X[] are the same, Y[] are specially
             * calculated to ensure simple form of all types of errors)
             * and check correctness of the errors calculated by subroutines
             *
             * First pass is done with zero Y[], other passes - with random Y[].
             * It should test both ability to correctly calculate errors and
             * ability to not fail while working with zeros :)
             */
            n = 4*maxn;
            if( pass==1 )
            {
                v1 = 0;
                v2 = 0;
                v = 0;
            }
            else
            {
                v1 = ae_randomreal(_state);
                v2 = ae_randomreal(_state);
                v = 1+ae_randomreal(_state);
            }
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=maxn-1; i++)
            {
                x.ptr.p_double[4*i+0] = i;
                y.ptr.p_double[4*i+0] = v-v2;
                w.ptr.p_double[4*i+0] = 1;
                x.ptr.p_double[4*i+1] = i;
                y.ptr.p_double[4*i+1] = v-v1;
                w.ptr.p_double[4*i+1] = 1;
                x.ptr.p_double[4*i+2] = i;
                y.ptr.p_double[4*i+2] = v+v1;
                w.ptr.p_double[4*i+2] = 1;
                x.ptr.p_double[4*i+3] = i;
                y.ptr.p_double[4*i+3] = v+v2;
                w.ptr.p_double[4*i+3] = 1;
            }
            refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
            refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
            if( pass==1 )
            {
                refavgrel = 0;
            }
            else
            {
                refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
            }
            refmax = ae_maxreal(v1, v2, _state);
            
            /*
             * Test errors correctness
             */
            polynomialfit(&x, &y, n, m, &info, &p, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                s = barycentriccalc(&p, 0, _state);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
            }
        }
    }
    ae_frame_leave(_state);
}


static void testlsfitunit_testrationalfitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    ae_int_t maxn;
    ae_int_t passcount;
    barycentricinterpolant b1;
    barycentricinterpolant b2;
    ae_vector x;
    ae_vector x2;
    ae_vector y;
    ae_vector y2;
    ae_vector w;
    ae_vector w2;
    ae_vector xc;
    ae_vector yc;
    ae_vector dc;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    double t;
    double s;
    double v;
    double v0;
    double v1;
    double v2;
    ae_int_t info;
    ae_int_t info2;
    double xmin;
    double xmax;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    barycentricfitreport rep;
    barycentricfitreport rep2;

    ae_frame_make(_state, &_frame_block);
    _barycentricinterpolant_init(&b1, _state, ae_true);
    _barycentricinterpolant_init(&b2, _state, ae_true);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&x2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _barycentricfitreport_init(&rep, _state, ae_true);
    _barycentricfitreport_init(&rep2, _state, ae_true);

    *fiterrors = ae_false;
    
    /*
     * PassCount        number of repeated passes
     * Threshold        error tolerance
     * LipschitzTol     Lipschitz constant increase allowed
     *                  when calculating constant on a twice denser grid
     */
    passcount = 5;
    maxn = 15;
    threshold = 1000000*ae_machineepsilon;
    
    /*
     * Test rational fitting:
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=2; n<=maxn; n++)
        {
            
            /*
             * N=M+K fitting (i.e. interpolation)
             */
            for(k=0; k<=n-1; k++)
            {
                ae_vector_set_length(&x, n-k, _state);
                ae_vector_set_length(&y, n-k, _state);
                ae_vector_set_length(&w, n-k, _state);
                if( k>0 )
                {
                    ae_vector_set_length(&xc, k, _state);
                    ae_vector_set_length(&yc, k, _state);
                    ae_vector_set_length(&dc, k, _state);
                }
                for(i=0; i<=n-k-1; i++)
                {
                    x.ptr.p_double[i] = (double)i/(double)(n-1);
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                for(i=0; i<=k-1; i++)
                {
                    xc.ptr.p_double[i] = (double)(n-k+i)/(double)(n-1);
                    yc.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    dc.ptr.p_int[i] = 0;
                }
                barycentricfitfloaterhormannwc(&x, &y, &w, n-k, &xc, &yc, &dc, k, n, &info, &b1, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, x.ptr.p_double[i], _state)-y.ptr.p_double[i], _state),threshold);
                    }
                    for(i=0; i<=k-1; i++)
                    {
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(barycentriccalc(&b1, xc.ptr.p_double[i], _state)-yc.ptr.p_double[i], _state),threshold);
                    }
                }
            }
            
            /*
             * Testing constraints on derivatives:
             * * several M's are tried
             * * several K's are tried - 1, 2.
             * * constraints at the ends of the interval
             */
            for(m=3; m<=5; m++)
            {
                for(k=1; k<=2; k++)
                {
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&y, n, _state);
                    ae_vector_set_length(&w, n, _state);
                    ae_vector_set_length(&xc, 2, _state);
                    ae_vector_set_length(&yc, 2, _state);
                    ae_vector_set_length(&dc, 2, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        w.ptr.p_double[i] = 1+ae_randomreal(_state);
                    }
                    xc.ptr.p_double[0] = -1;
                    yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                    dc.ptr.p_int[0] = 0;
                    xc.ptr.p_double[1] = 1;
                    yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                    dc.ptr.p_int[1] = 0;
                    barycentricfitfloaterhormannwc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &b1, &rep, _state);
                    if( info<=0 )
                    {
                        *fiterrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=k-1; i++)
                        {
                            barycentricdiff1(&b1, xc.ptr.p_double[i], &v0, &v1, _state);
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v0-yc.ptr.p_double[i], _state),threshold);
                        }
                    }
                }
            }
        }
    }
    for(m=2; m<=8; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * General fitting
             *
             * interpolating function through M nodes should have
             * greater RMS error than fitting it through the same M nodes
             */
            n = 100;
            ae_vector_set_length(&x2, n, _state);
            ae_vector_set_length(&y2, n, _state);
            ae_vector_set_length(&w2, n, _state);
            xmin = ae_maxrealnumber;
            xmax = -ae_maxrealnumber;
            for(i=0; i<=n-1; i++)
            {
                x2.ptr.p_double[i] = 2*ae_pi*ae_randomreal(_state);
                y2.ptr.p_double[i] = ae_sin(x2.ptr.p_double[i], _state);
                w2.ptr.p_double[i] = 1;
                xmin = ae_minreal(xmin, x2.ptr.p_double[i], _state);
                xmax = ae_maxreal(xmax, x2.ptr.p_double[i], _state);
            }
            ae_vector_set_length(&x, m, _state);
            ae_vector_set_length(&y, m, _state);
            for(i=0; i<=m-1; i++)
            {
                x.ptr.p_double[i] = xmin+(xmax-xmin)*i/(m-1);
                y.ptr.p_double[i] = ae_sin(x.ptr.p_double[i], _state);
            }
            barycentricbuildfloaterhormann(&x, &y, m, 3, &b1, _state);
            barycentricfitfloaterhormannwc(&x2, &y2, &w2, n, &xc, &yc, &dc, 0, m, &info, &b2, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate B1 (interpolant) RMS error, compare with B2 error
                 */
                v1 = 0;
                v2 = 0;
                for(i=0; i<=n-1; i++)
                {
                    v1 = v1+ae_sqr(barycentriccalc(&b1, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                    v2 = v2+ae_sqr(barycentriccalc(&b2, x2.ptr.p_double[i], _state)-y2.ptr.p_double[i], _state);
                }
                v1 = ae_sqrt(v1/n, _state);
                v2 = ae_sqrt(v2/n, _state);
                *fiterrors = *fiterrors||ae_fp_greater(v2,v1);
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v2-rep.rmserror, _state),threshold);
            }
            
            /*
             * compare weighted and non-weighted
             */
            n = 20;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = 1;
            }
            barycentricfitfloaterhormannwc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info, &b1, &rep, _state);
            barycentricfitfloaterhormann(&x, &y, n, m, &info2, &b2, &rep2, _state);
            if( info<=0||info2<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * calculate B1 (interpolant), compare with B2
                 * compare RMS errors
                 */
                t = 2*ae_randomreal(_state)-1;
                v1 = barycentriccalc(&b1, t, _state);
                v2 = barycentriccalc(&b2, t, _state);
                *fiterrors = *fiterrors||ae_fp_neq(v2,v1);
                *fiterrors = *fiterrors||ae_fp_neq(rep.rmserror,rep2.rmserror);
                *fiterrors = *fiterrors||ae_fp_neq(rep.avgerror,rep2.avgerror);
                *fiterrors = *fiterrors||ae_fp_neq(rep.avgrelerror,rep2.avgrelerror);
                *fiterrors = *fiterrors||ae_fp_neq(rep.maxerror,rep2.maxerror);
            }
        }
    }
    for(pass=1; pass<=passcount; pass++)
    {
        ae_assert(passcount>=2, "PassCount should be 2 or greater!", _state);
        
        /*
         * solve simple task (all X[] are the same, Y[] are specially
         * calculated to ensure simple form of all types of errors)
         * and check correctness of the errors calculated by subroutines
         *
         * First pass is done with zero Y[], other passes - with random Y[].
         * It should test both ability to correctly calculate errors and
         * ability to not fail while working with zeros :)
         */
        n = 4;
        if( pass==1 )
        {
            v1 = 0;
            v2 = 0;
            v = 0;
        }
        else
        {
            v1 = ae_randomreal(_state);
            v2 = ae_randomreal(_state);
            v = 1+ae_randomreal(_state);
        }
        ae_vector_set_length(&x, 4, _state);
        ae_vector_set_length(&y, 4, _state);
        ae_vector_set_length(&w, 4, _state);
        x.ptr.p_double[0] = 0;
        y.ptr.p_double[0] = v-v2;
        w.ptr.p_double[0] = 1;
        x.ptr.p_double[1] = 0;
        y.ptr.p_double[1] = v-v1;
        w.ptr.p_double[1] = 1;
        x.ptr.p_double[2] = 0;
        y.ptr.p_double[2] = v+v1;
        w.ptr.p_double[2] = 1;
        x.ptr.p_double[3] = 0;
        y.ptr.p_double[3] = v+v2;
        w.ptr.p_double[3] = 1;
        refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
        refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
        if( pass==1 )
        {
            refavgrel = 0;
        }
        else
        {
            refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
        }
        refmax = ae_maxreal(v1, v2, _state);
        
        /*
         * Test errors correctness
         */
        barycentricfitfloaterhormann(&x, &y, 4, 2, &info, &b1, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = barycentriccalc(&b1, 0, _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


static void testlsfitunit_testsplinefitting(ae_bool* fiterrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    double nonstrictthreshold;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t k;
    ae_int_t pass;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector w2;
    ae_vector xc;
    ae_vector yc;
    ae_vector d;
    ae_vector dc;
    double sa;
    double sb;
    ae_int_t info;
    ae_int_t info1;
    ae_int_t info2;
    spline1dinterpolant c;
    spline1dinterpolant c2;
    spline1dfitreport rep;
    spline1dfitreport rep2;
    double s;
    double ds;
    double d2s;
    ae_int_t stype;
    double t;
    double v;
    double v1;
    double v2;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    double rho;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
    _spline1dinterpolant_init(&c, _state, ae_true);
    _spline1dinterpolant_init(&c2, _state, ae_true);
    _spline1dfitreport_init(&rep, _state, ae_true);
    _spline1dfitreport_init(&rep2, _state, ae_true);

    
    /*
     * Valyes:
     * * pass count
     * * threshold - for tests which must be satisfied exactly
     * * nonstrictthreshold - for approximate tests
     */
    passcount = 20;
    threshold = 10000*ae_machineepsilon;
    nonstrictthreshold = 1.0E-4;
    *fiterrors = ae_false;
    
    /*
     * Test fitting by Cubic and Hermite splines (obsolete, but still supported)
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Cubic splines
         * Ability to handle boundary constraints (1-4 constraints on F, dF/dx).
         */
        for(m=4; m<=8; m++)
        {
            for(k=1; k<=4; k++)
            {
                if( k>=m )
                {
                    continue;
                }
                n = 100;
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                ae_vector_set_length(&xc, 4, _state);
                ae_vector_set_length(&yc, 4, _state);
                ae_vector_set_length(&dc, 4, _state);
                sa = 1+ae_randomreal(_state);
                sb = 2*ae_randomreal(_state)-1;
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                xc.ptr.p_double[0] = sb;
                yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[0] = 0;
                xc.ptr.p_double[1] = sb;
                yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[1] = 1;
                xc.ptr.p_double[2] = sa+sb;
                yc.ptr.p_double[2] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[2] = 0;
                xc.ptr.p_double[3] = sa+sb;
                yc.ptr.p_double[3] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[3] = 1;
                spline1dfitcubicwc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &c, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    
                    /*
                     * Check that constraints are satisfied
                     */
                    for(i=0; i<=k-1; i++)
                    {
                        spline1ddiff(&c, xc.ptr.p_double[i], &s, &ds, &d2s, _state);
                        if( dc.ptr.p_int[i]==0 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==1 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==2 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[i], _state),threshold);
                        }
                    }
                }
            }
        }
        
        /*
         * Cubic splines
         * Ability to handle one internal constraint
         */
        for(m=4; m<=8; m++)
        {
            n = 100;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            ae_vector_set_length(&xc, 1, _state);
            ae_vector_set_length(&yc, 1, _state);
            ae_vector_set_length(&dc, 1, _state);
            sa = 1+ae_randomreal(_state);
            sb = 2*ae_randomreal(_state)-1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = 1+ae_randomreal(_state);
            }
            xc.ptr.p_double[0] = sa*ae_randomreal(_state)+sb;
            yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
            dc.ptr.p_int[0] = ae_randominteger(2, _state);
            spline1dfitcubicwc(&x, &y, &w, n, &xc, &yc, &dc, 1, m, &info, &c, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * Check that constraints are satisfied
                 */
                spline1ddiff(&c, xc.ptr.p_double[0], &s, &ds, &d2s, _state);
                if( dc.ptr.p_int[0]==0 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==1 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==2 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[0], _state),threshold);
                }
            }
        }
        
        /*
         * Hermite splines
         * Ability to handle boundary constraints (1-4 constraints on F, dF/dx).
         */
        for(m=4; m<=8; m++)
        {
            for(k=1; k<=4; k++)
            {
                if( k>=m )
                {
                    continue;
                }
                if( m%2!=0 )
                {
                    continue;
                }
                n = 100;
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                ae_vector_set_length(&xc, 4, _state);
                ae_vector_set_length(&yc, 4, _state);
                ae_vector_set_length(&dc, 4, _state);
                sa = 1+ae_randomreal(_state);
                sb = 2*ae_randomreal(_state)-1;
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                    y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                }
                xc.ptr.p_double[0] = sb;
                yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[0] = 0;
                xc.ptr.p_double[1] = sb;
                yc.ptr.p_double[1] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[1] = 1;
                xc.ptr.p_double[2] = sa+sb;
                yc.ptr.p_double[2] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[2] = 0;
                xc.ptr.p_double[3] = sa+sb;
                yc.ptr.p_double[3] = 2*ae_randomreal(_state)-1;
                dc.ptr.p_int[3] = 1;
                spline1dfithermitewc(&x, &y, &w, n, &xc, &yc, &dc, k, m, &info, &c, &rep, _state);
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    
                    /*
                     * Check that constraints are satisfied
                     */
                    for(i=0; i<=k-1; i++)
                    {
                        spline1ddiff(&c, xc.ptr.p_double[i], &s, &ds, &d2s, _state);
                        if( dc.ptr.p_int[i]==0 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==1 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[i], _state),threshold);
                        }
                        if( dc.ptr.p_int[i]==2 )
                        {
                            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[i], _state),threshold);
                        }
                    }
                }
            }
        }
        
        /*
         * Hermite splines
         * Ability to handle one internal constraint
         */
        for(m=4; m<=8; m++)
        {
            if( m%2!=0 )
            {
                continue;
            }
            n = 100;
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            ae_vector_set_length(&xc, 1, _state);
            ae_vector_set_length(&yc, 1, _state);
            ae_vector_set_length(&dc, 1, _state);
            sa = 1+ae_randomreal(_state);
            sb = 2*ae_randomreal(_state)-1;
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = 1+ae_randomreal(_state);
            }
            xc.ptr.p_double[0] = sa*ae_randomreal(_state)+sb;
            yc.ptr.p_double[0] = 2*ae_randomreal(_state)-1;
            dc.ptr.p_int[0] = ae_randominteger(2, _state);
            spline1dfithermitewc(&x, &y, &w, n, &xc, &yc, &dc, 1, m, &info, &c, &rep, _state);
            if( info<=0 )
            {
                *fiterrors = ae_true;
            }
            else
            {
                
                /*
                 * Check that constraints are satisfied
                 */
                spline1ddiff(&c, xc.ptr.p_double[0], &s, &ds, &d2s, _state);
                if( dc.ptr.p_int[0]==0 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==1 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(ds-yc.ptr.p_double[0], _state),threshold);
                }
                if( dc.ptr.p_int[0]==2 )
                {
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(d2s-yc.ptr.p_double[0], _state),threshold);
                }
            }
        }
    }
    for(m=4; m<=8; m++)
    {
        for(stype=0; stype<=1; stype++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                if( stype==1&&m%2!=0 )
                {
                    continue;
                }
                
                /*
                 * cubic/Hermite spline fitting:
                 * * generate "template spline" C2
                 * * generate 2*N points from C2, such that result of
                 *   ideal fit should be equal to C2
                 * * fit, store in C
                 * * compare C and C2
                 */
                sa = 1+ae_randomreal(_state);
                sb = 2*ae_randomreal(_state)-1;
                if( stype==0 )
                {
                    ae_vector_set_length(&x, m-2, _state);
                    ae_vector_set_length(&y, m-2, _state);
                    for(i=0; i<=m-2-1; i++)
                    {
                        x.ptr.p_double[i] = sa*i/(m-2-1)+sb;
                        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    spline1dbuildcubic(&x, &y, m-2, 1, 2*ae_randomreal(_state)-1, 1, 2*ae_randomreal(_state)-1, &c2, _state);
                }
                if( stype==1 )
                {
                    ae_vector_set_length(&x, m/2, _state);
                    ae_vector_set_length(&y, m/2, _state);
                    ae_vector_set_length(&d, m/2, _state);
                    for(i=0; i<=m/2-1; i++)
                    {
                        x.ptr.p_double[i] = sa*i/(m/2-1)+sb;
                        y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        d.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    spline1dbuildhermite(&x, &y, &d, m/2, &c2, _state);
                }
                n = 50;
                ae_vector_set_length(&x, 2*n, _state);
                ae_vector_set_length(&y, 2*n, _state);
                ae_vector_set_length(&w, 2*n, _state);
                for(i=0; i<=n-1; i++)
                {
                    
                    /*
                     * "if i=0" and "if i=1" are needed to
                     * synchronize interval size for C2 and
                     * spline being fitted (i.e. C).
                     */
                    t = ae_randomreal(_state);
                    x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                    if( i==0 )
                    {
                        x.ptr.p_double[i] = sb;
                    }
                    if( i==1 )
                    {
                        x.ptr.p_double[i] = sa+sb;
                    }
                    v = spline1dcalc(&c2, x.ptr.p_double[i], _state);
                    y.ptr.p_double[i] = v+t;
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                    x.ptr.p_double[n+i] = x.ptr.p_double[i];
                    y.ptr.p_double[n+i] = v-t;
                    w.ptr.p_double[n+i] = w.ptr.p_double[i];
                }
                if( stype==0 )
                {
                    spline1dfitcubicwc(&x, &y, &w, 2*n, &xc, &yc, &dc, 0, m, &info, &c, &rep, _state);
                }
                if( stype==1 )
                {
                    spline1dfithermitewc(&x, &y, &w, 2*n, &xc, &yc, &dc, 0, m, &info, &c, &rep, _state);
                }
                if( info<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = sa*ae_randomreal(_state)+sb;
                        *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(spline1dcalc(&c, v, _state)-spline1dcalc(&c2, v, _state), _state),threshold);
                    }
                }
            }
        }
    }
    for(m=4; m<=8; m++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            
            /*
             * prepare points/weights
             */
            sa = 1+ae_randomreal(_state);
            sb = 2*ae_randomreal(_state)-1;
            n = 10+ae_randominteger(10, _state);
            ae_vector_set_length(&x, n, _state);
            ae_vector_set_length(&y, n, _state);
            ae_vector_set_length(&w, n, _state);
            for(i=0; i<=n-1; i++)
            {
                x.ptr.p_double[i] = sa*ae_randomreal(_state)+sb;
                y.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                w.ptr.p_double[i] = 1;
            }
            
            /*
             * Fit cubic with unity weights, without weights, then compare
             */
            if( m>=4 )
            {
                spline1dfitcubicwc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info1, &c, &rep, _state);
                spline1dfitcubic(&x, &y, n, m, &info2, &c2, &rep2, _state);
                if( info1<=0||info2<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = sa*ae_randomreal(_state)+sb;
                        *fiterrors = *fiterrors||ae_fp_neq(spline1dcalc(&c, v, _state),spline1dcalc(&c2, v, _state));
                        *fiterrors = *fiterrors||ae_fp_neq(rep.taskrcond,rep2.taskrcond);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.rmserror,rep2.rmserror);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.avgerror,rep2.avgerror);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.avgrelerror,rep2.avgrelerror);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.maxerror,rep2.maxerror);
                    }
                }
            }
            
            /*
             * Fit Hermite with unity weights, without weights, then compare
             */
            if( m>=4&&m%2==0 )
            {
                spline1dfithermitewc(&x, &y, &w, n, &xc, &yc, &dc, 0, m, &info1, &c, &rep, _state);
                spline1dfithermite(&x, &y, n, m, &info2, &c2, &rep2, _state);
                if( info1<=0||info2<=0 )
                {
                    *fiterrors = ae_true;
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        v = sa*ae_randomreal(_state)+sb;
                        *fiterrors = *fiterrors||ae_fp_neq(spline1dcalc(&c, v, _state),spline1dcalc(&c2, v, _state));
                        *fiterrors = *fiterrors||ae_fp_neq(rep.taskrcond,rep2.taskrcond);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.rmserror,rep2.rmserror);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.avgerror,rep2.avgerror);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.avgrelerror,rep2.avgrelerror);
                        *fiterrors = *fiterrors||ae_fp_neq(rep.maxerror,rep2.maxerror);
                    }
                }
            }
        }
    }
    
    /*
     * check basic properties of penalized splines which are
     * preserved independently of Rho parameter.
     */
    for(m=4; m<=10; m++)
    {
        for(k=-5; k<=5; k++)
        {
            rho = k;
            
            /*
             * when we have two points (even with different weights),
             * resulting spline must be equal to the straight line
             */
            ae_vector_set_length(&x, 2, _state);
            ae_vector_set_length(&y, 2, _state);
            ae_vector_set_length(&w, 2, _state);
            x.ptr.p_double[0] = -0.5-ae_randomreal(_state);
            y.ptr.p_double[0] = 0.5+ae_randomreal(_state);
            w.ptr.p_double[0] = 1+ae_randomreal(_state);
            x.ptr.p_double[1] = 0.5+ae_randomreal(_state);
            y.ptr.p_double[1] = 0.5+ae_randomreal(_state);
            w.ptr.p_double[1] = 1+ae_randomreal(_state);
            spline1dfitpenalized(&x, &y, 2, m, rho, &info, &c, &rep, _state);
            if( info>0 )
            {
                v = 2*ae_randomreal(_state)-1;
                v1 = (v-x.ptr.p_double[0])/(x.ptr.p_double[1]-x.ptr.p_double[0])*y.ptr.p_double[1]+(v-x.ptr.p_double[1])/(x.ptr.p_double[0]-x.ptr.p_double[1])*y.ptr.p_double[0];
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-spline1dcalc(&c, v, _state), _state),nonstrictthreshold);
            }
            else
            {
                *fiterrors = ae_true;
            }
            spline1dfitpenalizedw(&x, &y, &w, 2, m, rho, &info, &c, &rep, _state);
            if( info>0 )
            {
                v = 2*ae_randomreal(_state)-1;
                v1 = (v-x.ptr.p_double[0])/(x.ptr.p_double[1]-x.ptr.p_double[0])*y.ptr.p_double[1]+(v-x.ptr.p_double[1])/(x.ptr.p_double[0]-x.ptr.p_double[1])*y.ptr.p_double[0];
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-spline1dcalc(&c, v, _state), _state),nonstrictthreshold);
            }
            else
            {
                *fiterrors = ae_true;
            }
            
            /*
             * spline fitting is invariant with respect to
             * scaling of weights (of course, ANY fitting algorithm
             * must be invariant, but we want to test this property
             * just to be sure that it is correctly implemented)
             */
            for(n=2; n<=2*m; n++)
            {
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                ae_vector_set_length(&w2, n, _state);
                s = 1+ae_exp(10*ae_randomreal(_state), _state);
                for(i=0; i<=n-1; i++)
                {
                    x.ptr.p_double[i] = (double)i/(double)(n-1);
                    y.ptr.p_double[i] = ae_randomreal(_state);
                    w.ptr.p_double[i] = 0.1+ae_randomreal(_state);
                    w2.ptr.p_double[i] = w.ptr.p_double[i]*s;
                }
                spline1dfitpenalizedw(&x, &y, &w, n, m, rho, &info, &c, &rep, _state);
                spline1dfitpenalizedw(&x, &y, &w2, n, m, rho, &info2, &c2, &rep2, _state);
                if( info>0&&info2>0 )
                {
                    v = ae_randomreal(_state);
                    v1 = spline1dcalc(&c, v, _state);
                    v2 = spline1dcalc(&c2, v, _state);
                    *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(v1-v2, _state),nonstrictthreshold);
                }
                else
                {
                    *fiterrors = ae_true;
                }
            }
        }
    }
    
    /*
     * Advanced proprties:
     * * penalized spline with M about 5*N and sufficiently small Rho
     *   must pass through all points on equidistant grid
     */
    for(n=2; n<=10; n++)
    {
        m = 5*n;
        rho = -5;
        ae_vector_set_length(&x, n, _state);
        ae_vector_set_length(&y, n, _state);
        ae_vector_set_length(&w, n, _state);
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = (double)i/(double)(n-1);
            y.ptr.p_double[i] = ae_randomreal(_state);
            w.ptr.p_double[i] = 0.1+ae_randomreal(_state);
        }
        spline1dfitpenalized(&x, &y, n, m, rho, &info, &c, &rep, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state),nonstrictthreshold);
            }
        }
        else
        {
            *fiterrors = ae_true;
        }
        spline1dfitpenalizedw(&x, &y, &w, n, m, rho, &info, &c, &rep, _state);
        if( info>0 )
        {
            for(i=0; i<=n-1; i++)
            {
                *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(y.ptr.p_double[i]-spline1dcalc(&c, x.ptr.p_double[i], _state), _state),nonstrictthreshold);
            }
        }
        else
        {
            *fiterrors = ae_true;
        }
    }
    
    /*
     * Check correctness of error reports
     */
    for(pass=1; pass<=passcount; pass++)
    {
        ae_assert(passcount>=2, "PassCount should be 2 or greater!", _state);
        
        /*
         * solve simple task (all X[] are the same, Y[] are specially
         * calculated to ensure simple form of all types of errors)
         * and check correctness of the errors calculated by subroutines
         *
         * First pass is done with zero Y[], other passes - with random Y[].
         * It should test both ability to correctly calculate errors and
         * ability to not fail while working with zeros :)
         */
        n = 4;
        if( pass==1 )
        {
            v1 = 0;
            v2 = 0;
            v = 0;
        }
        else
        {
            v1 = ae_randomreal(_state);
            v2 = ae_randomreal(_state);
            v = 1+ae_randomreal(_state);
        }
        ae_vector_set_length(&x, 4, _state);
        ae_vector_set_length(&y, 4, _state);
        ae_vector_set_length(&w, 4, _state);
        x.ptr.p_double[0] = 0;
        y.ptr.p_double[0] = v-v2;
        w.ptr.p_double[0] = 1;
        x.ptr.p_double[1] = 0;
        y.ptr.p_double[1] = v-v1;
        w.ptr.p_double[1] = 1;
        x.ptr.p_double[2] = 0;
        y.ptr.p_double[2] = v+v1;
        w.ptr.p_double[2] = 1;
        x.ptr.p_double[3] = 0;
        y.ptr.p_double[3] = v+v2;
        w.ptr.p_double[3] = 1;
        refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
        refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
        if( pass==1 )
        {
            refavgrel = 0;
        }
        else
        {
            refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
        }
        refmax = ae_maxreal(v1, v2, _state);
        
        /*
         * Test penalized spline
         */
        spline1dfitpenalizedw(&x, &y, &w, 4, 4, 0.0, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = spline1dcalc(&c, 0, _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
        
        /*
         * Test cubic fitting
         */
        spline1dfitcubic(&x, &y, 4, 4, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = spline1dcalc(&c, 0, _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
        
        /*
         * Test Hermite fitting
         */
        spline1dfithermite(&x, &y, 4, 4, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *fiterrors = ae_true;
        }
        else
        {
            s = spline1dcalc(&c, 0, _state);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(s-v, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *fiterrors = *fiterrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


static void testlsfitunit_testgeneralfitting(ae_bool* llserrors,
     ae_bool* nlserrors,
     ae_state *_state)
{
    ae_frame _frame_block;
    double threshold;
    double nlthreshold;
    ae_int_t maxn;
    ae_int_t maxm;
    ae_int_t passcount;
    ae_int_t n;
    ae_int_t m;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t pass;
    double xscale;
    double diffstep;
    ae_vector x;
    ae_vector y;
    ae_vector w;
    ae_vector w2;
    ae_vector c;
    ae_vector c2;
    ae_matrix a;
    ae_matrix a2;
    ae_matrix cm;
    double v;
    double v1;
    double v2;
    lsfitreport rep;
    lsfitreport rep2;
    ae_int_t info;
    ae_int_t info2;
    double refrms;
    double refavg;
    double refavgrel;
    double refmax;
    lsfitstate state;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a2, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&cm, 0, 0, DT_REAL, _state, ae_true);
    _lsfitreport_init(&rep, _state, ae_true);
    _lsfitreport_init(&rep2, _state, ae_true);
    _lsfitstate_init(&state, _state, ae_true);

    *llserrors = ae_false;
    *nlserrors = ae_false;
    threshold = 10000*ae_machineepsilon;
    nlthreshold = 0.00001;
    diffstep = 0.0001;
    maxn = 6;
    maxm = 6;
    passcount = 4;
    
    /*
     * Testing unconstrained least squares (linear/nonlinear)
     */
    for(n=1; n<=maxn; n++)
    {
        for(m=1; m<=maxm; m++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Solve non-degenerate linear least squares task
                 * Use Chebyshev basis. Its condition number is very good.
                 */
                ae_matrix_set_length(&a, n, m, _state);
                ae_vector_set_length(&x, n, _state);
                ae_vector_set_length(&y, n, _state);
                ae_vector_set_length(&w, n, _state);
                xscale = 0.9+0.1*ae_randomreal(_state);
                for(i=0; i<=n-1; i++)
                {
                    if( n==1 )
                    {
                        x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    else
                    {
                        x.ptr.p_double[i] = xscale*((double)(2*i)/(double)(n-1)-1);
                    }
                    y.ptr.p_double[i] = 3*x.ptr.p_double[i]+ae_exp(x.ptr.p_double[i], _state);
                    w.ptr.p_double[i] = 1+ae_randomreal(_state);
                    a.ptr.pp_double[i][0] = 1;
                    if( m>1 )
                    {
                        a.ptr.pp_double[i][1] = x.ptr.p_double[i];
                    }
                    for(j=2; j<=m-1; j++)
                    {
                        a.ptr.pp_double[i][j] = 2*x.ptr.p_double[i]*a.ptr.pp_double[i][j-1]-a.ptr.pp_double[i][j-2];
                    }
                }
                
                /*
                 * 1. test weighted fitting (optimality)
                 * 2. Solve degenerate least squares task built on the basis
                 *    of previous task
                 */
                lsfitlinearw(&y, &w, &a, n, m, &info, &c, &rep, _state);
                if( info<=0 )
                {
                    *llserrors = ae_true;
                }
                else
                {
                    *llserrors = *llserrors||!testlsfitunit_isglssolution(n, m, 0, &y, &w, &a, &cm, &c, _state);
                }
                ae_matrix_set_length(&a2, n, 2*m, _state);
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=m-1; j++)
                    {
                        a2.ptr.pp_double[i][2*j+0] = a.ptr.pp_double[i][j];
                        a2.ptr.pp_double[i][2*j+1] = a.ptr.pp_double[i][j];
                    }
                }
                lsfitlinearw(&y, &w, &a2, n, 2*m, &info, &c2, &rep, _state);
                if( info<=0 )
                {
                    *llserrors = ae_true;
                }
                else
                {
                    
                    /*
                     * test answer correctness using design matrix properties
                     * and previous task solution
                     */
                    for(j=0; j<=m-1; j++)
                    {
                        *llserrors = *llserrors||ae_fp_greater(ae_fabs(c2.ptr.p_double[2*j+0]+c2.ptr.p_double[2*j+1]-c.ptr.p_double[j], _state),threshold);
                    }
                }
                
                /*
                 * test non-weighted fitting
                 */
                ae_vector_set_length(&w2, n, _state);
                for(i=0; i<=n-1; i++)
                {
                    w2.ptr.p_double[i] = 1;
                }
                lsfitlinearw(&y, &w2, &a, n, m, &info, &c, &rep, _state);
                lsfitlinear(&y, &a, n, m, &info2, &c2, &rep2, _state);
                if( info<=0||info2<=0 )
                {
                    *llserrors = ae_true;
                }
                else
                {
                    
                    /*
                     * test answer correctness
                     */
                    for(j=0; j<=m-1; j++)
                    {
                        *llserrors = *llserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[j]-c2.ptr.p_double[j], _state),threshold);
                    }
                    *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.taskrcond-rep2.taskrcond, _state),threshold);
                }
                
                /*
                 * test nonlinear fitting on the linear task
                 * (only non-degenerate tasks are tested)
                 * and compare with answer from linear fitting subroutine
                 */
                if( n>=m )
                {
                    ae_vector_set_length(&c2, m, _state);
                    
                    /*
                     * test function/gradient/Hessian-based weighted fitting
                     */
                    lsfitlinearw(&y, &w, &a, n, m, &info, &c, &rep, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatewf(&a, &y, &w, &c2, n, m, m, diffstep, &state, _state);
                    lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 0, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatewfg(&a, &y, &w, &c2, n, m, m, ae_fp_greater(ae_randomreal(_state),0.5), &state, _state);
                    lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 1, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatewfgh(&a, &y, &w, &c2, n, m, m, &state, _state);
                    lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 2, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    
                    /*
                     * test gradient-only or Hessian-based fitting without weights
                     */
                    lsfitlinear(&y, &a, n, m, &info, &c, &rep, _state);
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatef(&a, &y, &c2, n, m, m, diffstep, &state, _state);
                    lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 0, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatefg(&a, &y, &c2, n, m, m, ae_fp_greater(ae_randomreal(_state),0.5), &state, _state);
                    lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 1, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                    for(i=0; i<=m-1; i++)
                    {
                        c2.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                    }
                    lsfitcreatefgh(&a, &y, &c2, n, m, m, &state, _state);
                    lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
                    testlsfitunit_fitlinearnonlinear(m, 2, &a, &state, nlserrors, _state);
                    lsfitresults(&state, &info, &c2, &rep2, _state);
                    if( info<=0 )
                    {
                        *nlserrors = ae_true;
                    }
                    else
                    {
                        for(i=0; i<=m-1; i++)
                        {
                            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[i]-c2.ptr.p_double[i], _state),100*nlthreshold);
                        }
                    }
                }
            }
        }
        
        /*
         * test correctness of the RCond field
         */
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        ae_vector_set_length(&x, n-1+1, _state);
        ae_vector_set_length(&y, n-1+1, _state);
        ae_vector_set_length(&w, n-1+1, _state);
        v1 = ae_maxrealnumber;
        v2 = ae_minrealnumber;
        for(i=0; i<=n-1; i++)
        {
            x.ptr.p_double[i] = 0.1+0.9*ae_randomreal(_state);
            y.ptr.p_double[i] = 0.1+0.9*ae_randomreal(_state);
            w.ptr.p_double[i] = 1;
            for(j=0; j<=n-1; j++)
            {
                if( i==j )
                {
                    a.ptr.pp_double[i][i] = 0.1+0.9*ae_randomreal(_state);
                    v1 = ae_minreal(v1, a.ptr.pp_double[i][i], _state);
                    v2 = ae_maxreal(v2, a.ptr.pp_double[i][i], _state);
                }
                else
                {
                    a.ptr.pp_double[i][j] = 0;
                }
            }
        }
        lsfitlinearw(&y, &w, &a, n, n, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *llserrors = ae_true;
        }
        else
        {
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.taskrcond-v1/v2, _state),threshold);
        }
    }
    
    /*
     * Test constrained least squares
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            for(m=1; m<=maxm; m++)
            {
                
                /*
                 * test for K<>0
                 */
                for(k=1; k<=m-1; k++)
                {
                    
                    /*
                     * Prepare Chebyshev basis. Its condition number is very good.
                     * Prepare constraints (random numbers)
                     */
                    ae_matrix_set_length(&a, n, m, _state);
                    ae_vector_set_length(&x, n, _state);
                    ae_vector_set_length(&y, n, _state);
                    ae_vector_set_length(&w, n, _state);
                    xscale = 0.9+0.1*ae_randomreal(_state);
                    for(i=0; i<=n-1; i++)
                    {
                        if( n==1 )
                        {
                            x.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                        }
                        else
                        {
                            x.ptr.p_double[i] = xscale*((double)(2*i)/(double)(n-1)-1);
                        }
                        y.ptr.p_double[i] = 3*x.ptr.p_double[i]+ae_exp(x.ptr.p_double[i], _state);
                        w.ptr.p_double[i] = 1+ae_randomreal(_state);
                        a.ptr.pp_double[i][0] = 1;
                        if( m>1 )
                        {
                            a.ptr.pp_double[i][1] = x.ptr.p_double[i];
                        }
                        for(j=2; j<=m-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*x.ptr.p_double[i]*a.ptr.pp_double[i][j-1]-a.ptr.pp_double[i][j-2];
                        }
                    }
                    ae_matrix_set_length(&cm, k, m+1, _state);
                    for(i=0; i<=k-1; i++)
                    {
                        for(j=0; j<=m; j++)
                        {
                            cm.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                        }
                    }
                    
                    /*
                     * Solve constrained task
                     */
                    lsfitlinearwc(&y, &w, &a, &cm, n, m, k, &info, &c, &rep, _state);
                    if( info<=0 )
                    {
                        *llserrors = ae_true;
                    }
                    else
                    {
                        *llserrors = *llserrors||!testlsfitunit_isglssolution(n, m, k, &y, &w, &a, &cm, &c, _state);
                    }
                    
                    /*
                     * test non-weighted fitting
                     */
                    ae_vector_set_length(&w2, n, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        w2.ptr.p_double[i] = 1;
                    }
                    lsfitlinearwc(&y, &w2, &a, &cm, n, m, k, &info, &c, &rep, _state);
                    lsfitlinearc(&y, &a, &cm, n, m, k, &info2, &c2, &rep2, _state);
                    if( info<=0||info2<=0 )
                    {
                        *llserrors = ae_true;
                    }
                    else
                    {
                        
                        /*
                         * test answer correctness
                         */
                        for(j=0; j<=m-1; j++)
                        {
                            *llserrors = *llserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[j]-c2.ptr.p_double[j], _state),threshold);
                        }
                        *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.taskrcond-rep2.taskrcond, _state),threshold);
                    }
                }
            }
        }
    }
    
    /*
     * nonlinear task for nonlinear fitting:
     *
     *     f(X,C) = 1/(1+C*X^2),
     *     C(true) = 2.
     */
    n = 100;
    ae_vector_set_length(&c, 1, _state);
    c.ptr.p_double[0] = 1+2*ae_randomreal(_state);
    ae_matrix_set_length(&a, n, 1, _state);
    ae_vector_set_length(&y, n, _state);
    for(i=0; i<=n-1; i++)
    {
        a.ptr.pp_double[i][0] = 4*ae_randomreal(_state)-2;
        y.ptr.p_double[i] = 1/(1+2*ae_sqr(a.ptr.pp_double[i][0], _state));
    }
    lsfitcreatefg(&a, &y, &c, n, 1, 1, ae_true, &state, _state);
    lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
    while(lsfititeration(&state, _state))
    {
        if( state.needf )
        {
            state.f = 1/(1+state.c.ptr.p_double[0]*ae_sqr(state.x.ptr.p_double[0], _state));
        }
        if( state.needfg )
        {
            state.f = 1/(1+state.c.ptr.p_double[0]*ae_sqr(state.x.ptr.p_double[0], _state));
            state.g.ptr.p_double[0] = -ae_sqr(state.x.ptr.p_double[0], _state)/ae_sqr(1+state.c.ptr.p_double[0]*ae_sqr(state.x.ptr.p_double[0], _state), _state);
        }
    }
    lsfitresults(&state, &info, &c, &rep, _state);
    if( info<=0 )
    {
        *nlserrors = ae_true;
    }
    else
    {
        *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-2, _state),100*nlthreshold);
    }
    
    /*
     * solve simple task (fitting by constant function) and check
     * correctness of the errors calculated by subroutines
     */
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * test on task with non-zero Yi
         */
        n = 4;
        v1 = ae_randomreal(_state);
        v2 = ae_randomreal(_state);
        v = 1+ae_randomreal(_state);
        ae_vector_set_length(&c, 1, _state);
        c.ptr.p_double[0] = 1+2*ae_randomreal(_state);
        ae_matrix_set_length(&a, 4, 1, _state);
        ae_vector_set_length(&y, 4, _state);
        a.ptr.pp_double[0][0] = 1;
        y.ptr.p_double[0] = v-v2;
        a.ptr.pp_double[1][0] = 1;
        y.ptr.p_double[1] = v-v1;
        a.ptr.pp_double[2][0] = 1;
        y.ptr.p_double[2] = v+v1;
        a.ptr.pp_double[3][0] = 1;
        y.ptr.p_double[3] = v+v2;
        refrms = ae_sqrt((ae_sqr(v1, _state)+ae_sqr(v2, _state))/2, _state);
        refavg = (ae_fabs(v1, _state)+ae_fabs(v2, _state))/2;
        refavgrel = 0.25*(ae_fabs(v2, _state)/ae_fabs(v-v2, _state)+ae_fabs(v1, _state)/ae_fabs(v-v1, _state)+ae_fabs(v1, _state)/ae_fabs(v+v1, _state)+ae_fabs(v2, _state)/ae_fabs(v+v2, _state));
        refmax = ae_maxreal(v1, v2, _state);
        
        /*
         * Test LLS
         */
        lsfitlinear(&y, &a, 4, 1, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *llserrors = ae_true;
        }
        else
        {
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *llserrors = *llserrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
        
        /*
         * Test NLS
         */
        lsfitcreatefg(&a, &y, &c, 4, 1, 1, ae_true, &state, _state);
        lsfitsetcond(&state, 0.0, nlthreshold, 0, _state);
        while(lsfititeration(&state, _state))
        {
            if( state.needf )
            {
                state.f = state.c.ptr.p_double[0];
            }
            if( state.needfg )
            {
                state.f = state.c.ptr.p_double[0];
                state.g.ptr.p_double[0] = 1;
            }
        }
        lsfitresults(&state, &info, &c, &rep, _state);
        if( info<=0 )
        {
            *nlserrors = ae_true;
        }
        else
        {
            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(c.ptr.p_double[0]-v, _state),threshold);
            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(rep.rmserror-refrms, _state),threshold);
            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(rep.avgerror-refavg, _state),threshold);
            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(rep.avgrelerror-refavgrel, _state),threshold);
            *nlserrors = *nlserrors||ae_fp_greater(ae_fabs(rep.maxerror-refmax, _state),threshold);
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Tests whether C is solution of (possibly) constrained LLS problem
*************************************************************************/
static ae_bool testlsfitunit_isglssolution(ae_int_t n,
     ae_int_t m,
     ae_int_t k,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_matrix* cmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector _c;
    ae_int_t i;
    ae_int_t j;
    ae_vector c2;
    ae_vector sv;
    ae_vector deltac;
    ae_vector deltaproj;
    ae_matrix u;
    ae_matrix vt;
    double v;
    double s1;
    double s2;
    double s3;
    double delta;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init_copy(&_c, c, _state, ae_true);
    c = &_c;
    ae_vector_init(&c2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sv, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&deltac, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&deltaproj, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);

    
    /*
     * Setup.
     * Threshold is small because CMatrix may be ill-conditioned
     */
    delta = 0.001;
    threshold = ae_sqrt(ae_machineepsilon, _state);
    ae_vector_set_length(&c2, m, _state);
    ae_vector_set_length(&deltac, m, _state);
    ae_vector_set_length(&deltaproj, m, _state);
    
    /*
     * test whether C is feasible point or not (projC must be close to C)
     */
    for(i=0; i<=k-1; i++)
    {
        v = ae_v_dotproduct(&cmatrix->ptr.pp_double[i][0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        if( ae_fp_greater(ae_fabs(v-cmatrix->ptr.pp_double[i][m], _state),threshold) )
        {
            result = ae_false;
            ae_frame_leave(_state);
            return result;
        }
    }
    
    /*
     * find orthogonal basis of Null(CMatrix) (stored in rows from K to M-1)
     */
    if( k>0 )
    {
        rmatrixsvd(cmatrix, k, m, 0, 2, 2, &sv, &u, &vt, _state);
    }
    
    /*
     * Test result
     */
    result = ae_true;
    s1 = testlsfitunit_getglserror(n, m, y, w, fmatrix, c, _state);
    for(j=0; j<=m-1; j++)
    {
        
        /*
         * prepare modification of C which leave us in the feasible set.
         *
         * let deltaC be increment on Jth coordinate, then project
         * deltaC in the Null(CMatrix) and store result in DeltaProj
         */
        ae_v_move(&c2.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        for(i=0; i<=m-1; i++)
        {
            if( i==j )
            {
                deltac.ptr.p_double[i] = delta;
            }
            else
            {
                deltac.ptr.p_double[i] = 0;
            }
        }
        if( k==0 )
        {
            ae_v_move(&deltaproj.ptr.p_double[0], 1, &deltac.ptr.p_double[0], 1, ae_v_len(0,m-1));
        }
        else
        {
            for(i=0; i<=m-1; i++)
            {
                deltaproj.ptr.p_double[i] = 0;
            }
            for(i=k; i<=m-1; i++)
            {
                v = ae_v_dotproduct(&vt.ptr.pp_double[i][0], 1, &deltac.ptr.p_double[0], 1, ae_v_len(0,m-1));
                ae_v_addd(&deltaproj.ptr.p_double[0], 1, &vt.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
            }
        }
        
        /*
         * now we have DeltaProj such that if C is feasible,
         * then C+DeltaProj is feasible too
         */
        ae_v_move(&c2.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        ae_v_add(&c2.ptr.p_double[0], 1, &deltaproj.ptr.p_double[0], 1, ae_v_len(0,m-1));
        s2 = testlsfitunit_getglserror(n, m, y, w, fmatrix, &c2, _state);
        ae_v_move(&c2.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        ae_v_sub(&c2.ptr.p_double[0], 1, &deltaproj.ptr.p_double[0], 1, ae_v_len(0,m-1));
        s3 = testlsfitunit_getglserror(n, m, y, w, fmatrix, &c2, _state);
        result = (result&&ae_fp_greater_eq(s2,s1/(1+threshold)))&&ae_fp_greater_eq(s3,s1/(1+threshold));
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests whether C is solution of LLS problem
*************************************************************************/
static double testlsfitunit_getglserror(ae_int_t n,
     ae_int_t m,
     /* Real    */ ae_vector* y,
     /* Real    */ ae_vector* w,
     /* Real    */ ae_matrix* fmatrix,
     /* Real    */ ae_vector* c,
     ae_state *_state)
{
    ae_int_t i;
    double v;
    double result;


    result = 0;
    for(i=0; i<=n-1; i++)
    {
        v = ae_v_dotproduct(&fmatrix->ptr.pp_double[i][0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
        result = result+ae_sqr(w->ptr.p_double[i]*(v-y->ptr.p_double[i]), _state);
    }
    return result;
}


/*************************************************************************
Subroutine for nonlinear fitting of linear problem

DerAvailable:
* 0     when only function value should be used
* 1     when we can provide gradient/function
* 2     when we can provide Hessian/gradient/function

When something which is not permitted by DerAvailable is requested,
this function sets NLSErrors to True.
*************************************************************************/
static void testlsfitunit_fitlinearnonlinear(ae_int_t m,
     ae_int_t deravailable,
     /* Real    */ ae_matrix* xy,
     lsfitstate* state,
     ae_bool* nlserrors,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double v;


    while(lsfititeration(state, _state))
    {
        
        /*
         * assume that one and only one of flags is set
         * test that we didn't request hessian in hessian-free setting
         */
        if( deravailable<1&&state->needfg )
        {
            *nlserrors = ae_true;
        }
        if( deravailable<2&&state->needfgh )
        {
            *nlserrors = ae_true;
        }
        i = 0;
        if( state->needf )
        {
            i = i+1;
        }
        if( state->needfg )
        {
            i = i+1;
        }
        if( state->needfgh )
        {
            i = i+1;
        }
        if( i!=1 )
        {
            *nlserrors = ae_true;
        }
        
        /*
         * test that PointIndex is consistent with actual point passed
         */
        for(i=0; i<=m-1; i++)
        {
            *nlserrors = *nlserrors||ae_fp_neq(xy->ptr.pp_double[state->pointindex][i],state->x.ptr.p_double[i]);
        }
        
        /*
         * calculate
         */
        if( state->needf )
        {
            v = ae_v_dotproduct(&state->x.ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,m-1));
            state->f = v;
            continue;
        }
        if( state->needfg )
        {
            v = ae_v_dotproduct(&state->x.ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,m-1));
            state->f = v;
            ae_v_move(&state->g.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,m-1));
            continue;
        }
        if( state->needfgh )
        {
            v = ae_v_dotproduct(&state->x.ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,m-1));
            state->f = v;
            ae_v_move(&state->g.ptr.p_double[0], 1, &state->x.ptr.p_double[0], 1, ae_v_len(0,m-1));
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=m-1; j++)
                {
                    state->h.ptr.pp_double[i][j] = 0;
                }
            }
            continue;
        }
    }
}



static void testpsplineunit_unsetp2(pspline2interpolant* p,
     ae_state *_state);
static void testpsplineunit_unsetp3(pspline3interpolant* p,
     ae_state *_state);
static void testpsplineunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state);





ae_bool testpspline(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool p2errors;
    ae_bool p3errors;
    double nonstrictthreshold;
    double threshold;
    ae_int_t passcount;
    double lstep;
    double h;
    ae_int_t maxn;
    ae_int_t periodicity;
    ae_int_t skind;
    ae_int_t pkind;
    ae_bool periodic;
    double a;
    double b;
    ae_int_t n;
    ae_int_t tmpn;
    ae_int_t i;
    double vx;
    double vy;
    double vz;
    double vx2;
    double vy2;
    double vz2;
    double vdx;
    double vdy;
    double vdz;
    double vdx2;
    double vdy2;
    double vdz2;
    double vd2x;
    double vd2y;
    double vd2z;
    double vd2x2;
    double vd2y2;
    double vd2z2;
    double v0;
    double v1;
    ae_vector x;
    ae_vector y;
    ae_vector z;
    ae_vector t;
    ae_vector t2;
    ae_vector t3;
    ae_matrix xy;
    ae_matrix xyz;
    pspline2interpolant p2;
    pspline3interpolant p3;
    spline1dinterpolant s;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t2, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t3, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&xyz, 0, 0, DT_REAL, _state, ae_true);
    _pspline2interpolant_init(&p2, _state, ae_true);
    _pspline3interpolant_init(&p3, _state, ae_true);
    _spline1dinterpolant_init(&s, _state, ae_true);

    waserrors = ae_false;
    passcount = 20;
    lstep = 0.005;
    h = 0.00001;
    maxn = 10;
    threshold = 10000*ae_machineepsilon;
    nonstrictthreshold = 0.00001;
    p2errors = ae_false;
    p3errors = ae_false;
    
    /*
     * Test basic properties of 2- and 3-dimensional splines:
     * * PSpline2ParameterValues() properties
     * * values at nodes
     * * for periodic splines - periodicity properties
     *
     * Variables used:
     * * N              points count
     * * SKind          spline
     * * PKind          parameterization
     * * Periodicity    whether we have periodic spline or not
     */
    for(n=2; n<=maxn; n++)
    {
        for(skind=0; skind<=2; skind++)
        {
            for(pkind=0; pkind<=2; pkind++)
            {
                for(periodicity=0; periodicity<=1; periodicity++)
                {
                    periodic = periodicity==1;
                    
                    /*
                     * skip unsupported combinations of parameters
                     */
                    if( periodic&&n<3 )
                    {
                        continue;
                    }
                    if( periodic&&skind==0 )
                    {
                        continue;
                    }
                    if( n<5&&skind==0 )
                    {
                        continue;
                    }
                    
                    /*
                     * init
                     */
                    ae_matrix_set_length(&xy, n, 2, _state);
                    ae_matrix_set_length(&xyz, n, 3, _state);
                    taskgenint1dequidist(-1, 1, n, &t2, &x, _state);
                    ae_v_move(&xy.ptr.pp_double[0][0], xy.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_v_move(&xyz.ptr.pp_double[0][0], xyz.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    taskgenint1dequidist(-1, 1, n, &t2, &y, _state);
                    ae_v_move(&xy.ptr.pp_double[0][1], xy.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    ae_v_move(&xyz.ptr.pp_double[0][1], xyz.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    taskgenint1dequidist(-1, 1, n, &t2, &z, _state);
                    ae_v_move(&xyz.ptr.pp_double[0][2], xyz.stride, &z.ptr.p_double[0], 1, ae_v_len(0,n-1));
                    testpsplineunit_unsetp2(&p2, _state);
                    testpsplineunit_unsetp3(&p3, _state);
                    if( periodic )
                    {
                        pspline2buildperiodic(&xy, n, skind, pkind, &p2, _state);
                        pspline3buildperiodic(&xyz, n, skind, pkind, &p3, _state);
                    }
                    else
                    {
                        pspline2build(&xy, n, skind, pkind, &p2, _state);
                        pspline3build(&xyz, n, skind, pkind, &p3, _state);
                    }
                    
                    /*
                     * PSpline2ParameterValues() properties
                     */
                    pspline2parametervalues(&p2, &tmpn, &t2, _state);
                    if( tmpn!=n )
                    {
                        p2errors = ae_true;
                        continue;
                    }
                    pspline3parametervalues(&p3, &tmpn, &t3, _state);
                    if( tmpn!=n )
                    {
                        p3errors = ae_true;
                        continue;
                    }
                    p2errors = p2errors||ae_fp_neq(t2.ptr.p_double[0],0);
                    p3errors = p3errors||ae_fp_neq(t3.ptr.p_double[0],0);
                    for(i=1; i<=n-1; i++)
                    {
                        p2errors = p2errors||ae_fp_less_eq(t2.ptr.p_double[i],t2.ptr.p_double[i-1]);
                        p3errors = p3errors||ae_fp_less_eq(t3.ptr.p_double[i],t3.ptr.p_double[i-1]);
                    }
                    if( periodic )
                    {
                        p2errors = p2errors||ae_fp_greater_eq(t2.ptr.p_double[n-1],1);
                        p3errors = p3errors||ae_fp_greater_eq(t3.ptr.p_double[n-1],1);
                    }
                    else
                    {
                        p2errors = p2errors||ae_fp_neq(t2.ptr.p_double[n-1],1);
                        p3errors = p3errors||ae_fp_neq(t3.ptr.p_double[n-1],1);
                    }
                    
                    /*
                     * Now we have parameter values stored at T,
                     * and want to test whether the actully correspond to
                     * points
                     */
                    for(i=0; i<=n-1; i++)
                    {
                        
                        /*
                         * 2-dimensional test
                         */
                        pspline2calc(&p2, t2.ptr.p_double[i], &vx, &vy, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                        
                        /*
                         * 3-dimensional test
                         */
                        pspline3calc(&p3, t3.ptr.p_double[i], &vx, &vy, &vz, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                    }
                    
                    /*
                     * Test periodicity (if needed)
                     */
                    if( periodic )
                    {
                        
                        /*
                         * periodicity at nodes
                         */
                        for(i=0; i<=n-1; i++)
                        {
                            
                            /*
                             * 2-dimensional test
                             */
                            pspline2calc(&p2, t2.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vy, _state);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            pspline2diff(&p2, t2.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vy, &vdy, _state);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            pspline2diff2(&p2, t2.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, _state);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            
                            /*
                             * 3-dimensional test
                             */
                            pspline3calc(&p3, t3.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vy, &vz, _state);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                            pspline3diff(&p3, t3.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vy, &vdy, &vz, &vdz, _state);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                            pspline3diff2(&p3, t3.ptr.p_double[i]+ae_randominteger(10, _state)-5, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, &vz, &vdz, &vd2z, _state);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vx-x.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vy-y.ptr.p_double[i], _state),threshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vz-z.ptr.p_double[i], _state),threshold);
                        }
                        
                        /*
                         * periodicity between nodes
                         */
                        v0 = ae_randomreal(_state);
                        pspline2calc(&p2, v0, &vx, &vy, _state);
                        pspline2calc(&p2, v0+ae_randominteger(10, _state)-5, &vx2, &vy2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        pspline3calc(&p3, v0, &vx, &vy, &vz, _state);
                        pspline3calc(&p3, v0+ae_randominteger(10, _state)-5, &vx2, &vy2, &vz2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        
                        /*
                         * near-boundary test for continuity of function values and derivatives:
                         * 2-dimensional curve
                         */
                        ae_assert(skind==1||skind==2, "TEST: unexpected spline type!", _state);
                        v0 = 100*ae_machineepsilon;
                        v1 = 1-v0;
                        pspline2calc(&p2, v0, &vx, &vy, _state);
                        pspline2calc(&p2, v1, &vx2, &vy2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        pspline2diff(&p2, v0, &vx, &vdx, &vy, &vdy, _state);
                        pspline2diff(&p2, v1, &vx2, &vdx2, &vy2, &vdy2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        pspline2diff2(&p2, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, _state);
                        pspline2diff2(&p2, v1, &vx2, &vdx2, &vd2x2, &vy2, &vdy2, &vd2y2, _state);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        if( skind==2 )
                        {
                            
                            /*
                             * second derivative test only for cubic splines
                             */
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),nonstrictthreshold);
                            p2errors = p2errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),nonstrictthreshold);
                        }
                        
                        /*
                         * near-boundary test for continuity of function values and derivatives:
                         * 3-dimensional curve
                         */
                        ae_assert(skind==1||skind==2, "TEST: unexpected spline type!", _state);
                        v0 = 100*ae_machineepsilon;
                        v1 = 1-v0;
                        pspline3calc(&p3, v0, &vx, &vy, &vz, _state);
                        pspline3calc(&p3, v1, &vx2, &vy2, &vz2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        pspline3diff(&p3, v0, &vx, &vdx, &vy, &vdy, &vz, &vdz, _state);
                        pspline3diff(&p3, v1, &vx2, &vdx2, &vy2, &vdy2, &vz2, &vdz2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),nonstrictthreshold);
                        pspline3diff2(&p3, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, &vz, &vdz, &vd2z, _state);
                        pspline3diff2(&p3, v1, &vx2, &vdx2, &vd2x2, &vy2, &vdy2, &vd2y2, &vz2, &vdz2, &vd2z2, _state);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),nonstrictthreshold);
                        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),nonstrictthreshold);
                        if( skind==2 )
                        {
                            
                            /*
                             * second derivative test only for cubic splines
                             */
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),nonstrictthreshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),nonstrictthreshold);
                            p3errors = p3errors||ae_fp_greater(ae_fabs(vd2z-vd2z2, _state),nonstrictthreshold);
                        }
                    }
                }
            }
        }
    }
    
    /*
     * Test differentiation, tangents, calculation between nodes.
     *
     * Because differentiation is done in parameterization/spline/periodicity
     * oblivious manner, we don't have to test all possible combinations
     * of spline types and parameterizations.
     *
     * Actually we test special combination with properties which allow us
     * to easily solve this problem:
     * * 2 (3) variables
     * * first variable is sampled from equidistant grid on [0,1]
     * * other variables are random
     * * uniform parameterization is used
     * * periodicity - none
     * * spline type - any (we use cubic splines)
     * Same problem allows us to test calculation BETWEEN nodes.
     */
    for(n=2; n<=maxn; n++)
    {
        
        /*
         * init
         */
        ae_matrix_set_length(&xy, n, 2, _state);
        ae_matrix_set_length(&xyz, n, 3, _state);
        taskgenint1dequidist(0, 1, n, &t, &x, _state);
        ae_v_move(&xy.ptr.pp_double[0][0], xy.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&xyz.ptr.pp_double[0][0], xyz.stride, &x.ptr.p_double[0], 1, ae_v_len(0,n-1));
        taskgenint1dequidist(0, 1, n, &t, &y, _state);
        ae_v_move(&xy.ptr.pp_double[0][1], xy.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
        ae_v_move(&xyz.ptr.pp_double[0][1], xyz.stride, &y.ptr.p_double[0], 1, ae_v_len(0,n-1));
        taskgenint1dequidist(0, 1, n, &t, &z, _state);
        ae_v_move(&xyz.ptr.pp_double[0][2], xyz.stride, &z.ptr.p_double[0], 1, ae_v_len(0,n-1));
        testpsplineunit_unsetp2(&p2, _state);
        testpsplineunit_unsetp3(&p3, _state);
        pspline2build(&xy, n, 2, 0, &p2, _state);
        pspline3build(&xyz, n, 2, 0, &p3, _state);
        
        /*
         * Test 2D/3D spline:
         * * build non-parametric cubic spline from T and X/Y
         * * calculate its value and derivatives at V0
         * * compare with Spline2Calc/Spline2Diff/Spline2Diff2
         * Because of task properties both variants should
         * return same answer.
         */
        v0 = ae_randomreal(_state);
        spline1dbuildcubic(&t, &x, n, 0, 0.0, 0, 0.0, &s, _state);
        spline1ddiff(&s, v0, &vx2, &vdx2, &vd2x2, _state);
        spline1dbuildcubic(&t, &y, n, 0, 0.0, 0, 0.0, &s, _state);
        spline1ddiff(&s, v0, &vy2, &vdy2, &vd2y2, _state);
        spline1dbuildcubic(&t, &z, n, 0, 0.0, 0, 0.0, &s, _state);
        spline1ddiff(&s, v0, &vz2, &vdz2, &vd2z2, _state);
        
        /*
         * 2D test
         */
        pspline2calc(&p2, v0, &vx, &vy, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        pspline2diff(&p2, v0, &vx, &vdx, &vy, &vdy, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        pspline2diff2(&p2, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),threshold);
        
        /*
         * 3D test
         */
        pspline3calc(&p3, v0, &vx, &vy, &vz, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
        pspline3diff(&p3, v0, &vx, &vdx, &vy, &vdy, &vz, &vdz, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),threshold);
        pspline3diff2(&p3, v0, &vx, &vdx, &vd2x, &vy, &vdy, &vd2y, &vz, &vdz, &vd2z, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vz2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdx-vdx2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdy-vdy2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vdz-vdz2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vd2x-vd2x2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vd2y-vd2y2, _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vd2z-vd2z2, _state),threshold);
        
        /*
         * Test tangents for 2D/3D
         */
        pspline2tangent(&p2, v0, &vx, &vy, _state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vx-vdx2/safepythag2(vdx2, vdy2, _state), _state),threshold);
        p2errors = p2errors||ae_fp_greater(ae_fabs(vy-vdy2/safepythag2(vdx2, vdy2, _state), _state),threshold);
        pspline3tangent(&p3, v0, &vx, &vy, &vz, _state);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vx-vdx2/safepythag3(vdx2, vdy2, vdz2, _state), _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vy-vdy2/safepythag3(vdx2, vdy2, vdz2, _state), _state),threshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(vz-vdz2/safepythag3(vdx2, vdy2, vdz2, _state), _state),threshold);
    }
    
    /*
     * Arc length test.
     *
     * Simple problem with easy solution (points on a straight line with
     * uniform parameterization).
     */
    for(n=2; n<=maxn; n++)
    {
        ae_matrix_set_length(&xy, n, 2, _state);
        ae_matrix_set_length(&xyz, n, 3, _state);
        for(i=0; i<=n-1; i++)
        {
            xy.ptr.pp_double[i][0] = i;
            xy.ptr.pp_double[i][1] = i;
            xyz.ptr.pp_double[i][0] = i;
            xyz.ptr.pp_double[i][1] = i;
            xyz.ptr.pp_double[i][2] = i;
        }
        pspline2build(&xy, n, 1, 0, &p2, _state);
        pspline3build(&xyz, n, 1, 0, &p3, _state);
        a = ae_randomreal(_state);
        b = ae_randomreal(_state);
        p2errors = p2errors||ae_fp_greater(ae_fabs(pspline2arclength(&p2, a, b, _state)-(b-a)*ae_sqrt(2, _state)*(n-1), _state),nonstrictthreshold);
        p3errors = p3errors||ae_fp_greater(ae_fabs(pspline3arclength(&p3, a, b, _state)-(b-a)*ae_sqrt(3, _state)*(n-1), _state),nonstrictthreshold);
    }
    
    /*
     * report
     */
    waserrors = p2errors||p3errors;
    if( !silent )
    {
        printf("TESTING SPLINE INTERPOLATION\n");
        
        /*
         * Normal tests
         */
        printf("2D TEST:                                 ");
        if( p2errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("3D TEST:                                 ");
        if( p3errors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testpsplineunit_unsetp2(pspline2interpolant* p,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;

    ae_frame_make(_state, &_frame_block);
    _pspline2interpolant_clear(p);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xy, 2, 2, _state);
    xy.ptr.pp_double[0][0] = -1;
    xy.ptr.pp_double[0][1] = -1;
    xy.ptr.pp_double[1][0] = 1;
    xy.ptr.pp_double[1][1] = 1;
    pspline2build(&xy, 2, 1, 0, p, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testpsplineunit_unsetp3(pspline3interpolant* p,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix xy;

    ae_frame_make(_state, &_frame_block);
    _pspline3interpolant_clear(p);
    ae_matrix_init(&xy, 0, 0, DT_REAL, _state, ae_true);

    ae_matrix_set_length(&xy, 2, 3, _state);
    xy.ptr.pp_double[0][0] = -1;
    xy.ptr.pp_double[0][1] = -1;
    xy.ptr.pp_double[0][2] = -1;
    xy.ptr.pp_double[1][0] = 1;
    xy.ptr.pp_double[1][1] = 1;
    xy.ptr.pp_double[1][2] = 1;
    pspline3build(&xy, 2, 1, 0, p, _state);
    ae_frame_leave(_state);
}


/*************************************************************************
Unsets real vector
*************************************************************************/
static void testpsplineunit_unset1d(/* Real    */ ae_vector* x,
     ae_state *_state)
{


    ae_vector_set_length(x, 1, _state);
    x->ptr.p_double[0] = 2*ae_randomreal(_state)-1;
}



static void testspline2dunit_lconst(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_int_t m,
     ae_int_t n,
     double lstep,
     double* lc,
     double* lcx,
     double* lcy,
     double* lcxy,
     ae_state *_state);
static void testspline2dunit_twodnumder(spline2dinterpolant* c,
     double x,
     double y,
     double h,
     double* f,
     double* fx,
     double* fy,
     double* fxy,
     ae_state *_state);
static ae_bool testspline2dunit_testunpack(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_state *_state);
static ae_bool testspline2dunit_testlintrans(spline2dinterpolant* c,
     double ax,
     double bx,
     double ay,
     double by,
     ae_state *_state);
static void testspline2dunit_unsetspline2d(spline2dinterpolant* c,
     ae_state *_state);





ae_bool testspline2d(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool blerrors;
    ae_bool bcerrors;
    ae_bool dserrors;
    ae_bool cperrors;
    ae_bool uperrors;
    ae_bool lterrors;
    ae_bool syerrors;
    ae_bool rlerrors;
    ae_bool rcerrors;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t jobtype;
    double lstep;
    double h;
    ae_vector x;
    ae_vector y;
    spline2dinterpolant c;
    spline2dinterpolant c2;
    ae_vector lx;
    ae_vector ly;
    ae_matrix f;
    ae_matrix fr;
    ae_matrix ft;
    double ax;
    double ay;
    double bx;
    double by;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t m;
    ae_int_t n2;
    ae_int_t m2;
    double err;
    double t;
    double t1;
    double t2;
    double l1;
    double l1x;
    double l1y;
    double l1xy;
    double l2;
    double l2x;
    double l2y;
    double l2xy;
    double fm;
    double f1;
    double f2;
    double f3;
    double f4;
    double v1;
    double v1x;
    double v1y;
    double v1xy;
    double v2;
    double v2x;
    double v2y;
    double v2xy;
    double mf;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    _spline2dinterpolant_init(&c, _state, ae_true);
    _spline2dinterpolant_init(&c2, _state, ae_true);
    ae_vector_init(&lx, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&ly, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&f, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&fr, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&ft, 0, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    passcount = 10;
    h = 0.00001;
    lstep = 0.001;
    blerrors = ae_false;
    bcerrors = ae_false;
    dserrors = ae_false;
    cperrors = ae_false;
    uperrors = ae_false;
    lterrors = ae_false;
    syerrors = ae_false;
    rlerrors = ae_false;
    rcerrors = ae_false;
    
    /*
     * Test: bilinear, bicubic
     */
    for(n=2; n<=7; n++)
    {
        for(m=2; m<=7; m++)
        {
            ae_vector_set_length(&x, n-1+1, _state);
            ae_vector_set_length(&y, m-1+1, _state);
            ae_vector_set_length(&lx, 2*n-2+1, _state);
            ae_vector_set_length(&ly, 2*m-2+1, _state);
            ae_matrix_set_length(&f, m-1+1, n-1+1, _state);
            ae_matrix_set_length(&ft, n-1+1, m-1+1, _state);
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Prepare task:
                 * * X and Y stores grid
                 * * F stores function values
                 * * LX and LY stores twice dense grid (for Lipschitz testing)
                 */
                ax = -1-ae_randomreal(_state);
                bx = 1+ae_randomreal(_state);
                ay = -1-ae_randomreal(_state);
                by = 1+ae_randomreal(_state);
                for(j=0; j<=n-1; j++)
                {
                    x.ptr.p_double[j] = 0.5*(bx+ax)-0.5*(bx-ax)*ae_cos(ae_pi*(2*j+1)/(2*n), _state);
                    if( j==0 )
                    {
                        x.ptr.p_double[j] = ax;
                    }
                    if( j==n-1 )
                    {
                        x.ptr.p_double[j] = bx;
                    }
                    lx.ptr.p_double[2*j] = x.ptr.p_double[j];
                    if( j>0 )
                    {
                        lx.ptr.p_double[2*j-1] = 0.5*(x.ptr.p_double[j]+x.ptr.p_double[j-1]);
                    }
                }
                for(j=0; j<=n-1; j++)
                {
                    k = ae_randominteger(n, _state);
                    if( k!=j )
                    {
                        t = x.ptr.p_double[j];
                        x.ptr.p_double[j] = x.ptr.p_double[k];
                        x.ptr.p_double[k] = t;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    y.ptr.p_double[i] = 0.5*(by+ay)-0.5*(by-ay)*ae_cos(ae_pi*(2*i+1)/(2*m), _state);
                    if( i==0 )
                    {
                        y.ptr.p_double[i] = ay;
                    }
                    if( i==m-1 )
                    {
                        y.ptr.p_double[i] = by;
                    }
                    ly.ptr.p_double[2*i] = y.ptr.p_double[i];
                    if( i>0 )
                    {
                        ly.ptr.p_double[2*i-1] = 0.5*(y.ptr.p_double[i]+y.ptr.p_double[i-1]);
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    k = ae_randominteger(m, _state);
                    if( k!=i )
                    {
                        t = y.ptr.p_double[i];
                        y.ptr.p_double[i] = y.ptr.p_double[k];
                        y.ptr.p_double[k] = t;
                    }
                }
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        f.ptr.pp_double[i][j] = ae_exp(0.6*x.ptr.p_double[j], _state)-ae_exp(-0.3*y.ptr.p_double[i]+0.08*x.ptr.p_double[j], _state)+2*ae_cos(ae_pi*(x.ptr.p_double[j]+1.2*y.ptr.p_double[i]), _state)+0.1*ae_cos(20*x.ptr.p_double[j]+15*y.ptr.p_double[i], _state);
                    }
                }
                
                /*
                 * Test bilinear interpolation:
                 * * interpolation at the nodes
                 * * linearity
                 * * continuity
                 * * differentiation in the inner points
                 */
                spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                err = 0;
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(f.ptr.pp_double[i][j]-spline2dcalc(&c, x.ptr.p_double[j], y.ptr.p_double[i], _state), _state), _state);
                    }
                }
                blerrors = blerrors||ae_fp_greater(err,10000*ae_machineepsilon);
                err = 0;
                for(i=0; i<=m-2; i++)
                {
                    for(j=0; j<=n-2; j++)
                    {
                        
                        /*
                         * Test for linearity between grid points
                         * (test point - geometric center of the cell)
                         */
                        fm = spline2dcalc(&c, lx.ptr.p_double[2*j+1], ly.ptr.p_double[2*i+1], _state);
                        f1 = spline2dcalc(&c, lx.ptr.p_double[2*j], ly.ptr.p_double[2*i], _state);
                        f2 = spline2dcalc(&c, lx.ptr.p_double[2*j+2], ly.ptr.p_double[2*i], _state);
                        f3 = spline2dcalc(&c, lx.ptr.p_double[2*j+2], ly.ptr.p_double[2*i+2], _state);
                        f4 = spline2dcalc(&c, lx.ptr.p_double[2*j], ly.ptr.p_double[2*i+2], _state);
                        err = ae_maxreal(err, ae_fabs(0.25*(f1+f2+f3+f4)-fm, _state), _state);
                    }
                }
                blerrors = blerrors||ae_fp_greater(err,10000*ae_machineepsilon);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep, &l1, &l1x, &l1y, &l1xy, _state);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep/3, &l2, &l2x, &l2y, &l2xy, _state);
                blerrors = blerrors||ae_fp_greater(l2/l1,1.2);
                err = 0;
                for(i=0; i<=m-2; i++)
                {
                    for(j=0; j<=n-2; j++)
                    {
                        spline2ddiff(&c, lx.ptr.p_double[2*j+1], ly.ptr.p_double[2*i+1], &v1, &v1x, &v1y, &v1xy, _state);
                        testspline2dunit_twodnumder(&c, lx.ptr.p_double[2*j+1], ly.ptr.p_double[2*i+1], h, &v2, &v2x, &v2y, &v2xy, _state);
                        err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1x-v2x, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1y-v2y, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1xy-v2xy, _state), _state);
                    }
                }
                dserrors = dserrors||ae_fp_greater(err,1.0E-3);
                uperrors = uperrors||!testspline2dunit_testunpack(&c, &lx, &ly, _state);
                lterrors = lterrors||!testspline2dunit_testlintrans(&c, ax, bx, ay, by, _state);
                
                /*
                 * Test bicubic interpolation.
                 * * interpolation at the nodes
                 * * smoothness
                 * * differentiation
                 */
                spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                err = 0;
                for(i=0; i<=m-1; i++)
                {
                    for(j=0; j<=n-1; j++)
                    {
                        err = ae_maxreal(err, ae_fabs(f.ptr.pp_double[i][j]-spline2dcalc(&c, x.ptr.p_double[j], y.ptr.p_double[i], _state), _state), _state);
                    }
                }
                bcerrors = bcerrors||ae_fp_greater(err,10000*ae_machineepsilon);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep, &l1, &l1x, &l1y, &l1xy, _state);
                testspline2dunit_lconst(&c, &lx, &ly, m, n, lstep/3, &l2, &l2x, &l2y, &l2xy, _state);
                bcerrors = bcerrors||ae_fp_greater(l2/l1,1.2);
                bcerrors = bcerrors||ae_fp_greater(l2x/l1x,1.2);
                bcerrors = bcerrors||ae_fp_greater(l2y/l1y,1.2);
                if( ae_fp_greater(l2xy,0.01)&&ae_fp_greater(l1xy,0.01) )
                {
                    
                    /*
                     * Cross-derivative continuity is tested only when
                     * bigger than 0.01. When the task size is too
                     * small, the d2F/dXdY is nearly zero and Lipschitz
                     * constant ratio is ill-conditioned.
                     */
                    bcerrors = bcerrors||ae_fp_greater(l2xy/l1xy,1.2);
                }
                err = 0;
                for(i=0; i<=2*m-2; i++)
                {
                    for(j=0; j<=2*n-2; j++)
                    {
                        spline2ddiff(&c, lx.ptr.p_double[j], ly.ptr.p_double[i], &v1, &v1x, &v1y, &v1xy, _state);
                        testspline2dunit_twodnumder(&c, lx.ptr.p_double[j], ly.ptr.p_double[i], h, &v2, &v2x, &v2y, &v2xy, _state);
                        err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1x-v2x, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1y-v2y, _state), _state);
                        err = ae_maxreal(err, ae_fabs(v1xy-v2xy, _state), _state);
                    }
                }
                dserrors = dserrors||ae_fp_greater(err,1.0E-3);
                uperrors = uperrors||!testspline2dunit_testunpack(&c, &lx, &ly, _state);
                lterrors = lterrors||!testspline2dunit_testlintrans(&c, ax, bx, ay, by, _state);
                
                /*
                 * Copy/Serialise test
                 */
                if( ae_fp_greater(ae_randomreal(_state),0.5) )
                {
                    spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                }
                else
                {
                    spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                }
                testspline2dunit_unsetspline2d(&c2, _state);
                spline2dcopy(&c, &c2, _state);
                err = 0;
                for(i=1; i<=5; i++)
                {
                    t1 = ax+(bx-ax)*ae_randomreal(_state);
                    t2 = ay+(by-ay)*ae_randomreal(_state);
                    err = ae_maxreal(err, ae_fabs(spline2dcalc(&c, t1, t2, _state)-spline2dcalc(&c2, t1, t2, _state), _state), _state);
                }
                cperrors = cperrors||ae_fp_greater(err,10000*ae_machineepsilon);
                
                /*
                 * Special symmetry test
                 */
                err = 0;
                for(jobtype=0; jobtype<=1; jobtype++)
                {
                    
                    /*
                     * Prepare
                     */
                    for(i=0; i<=m-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            ft.ptr.pp_double[j][i] = f.ptr.pp_double[i][j];
                        }
                    }
                    if( jobtype==0 )
                    {
                        spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                        spline2dbuildbilinear(&y, &x, &ft, n, m, &c2, _state);
                    }
                    else
                    {
                        spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                        spline2dbuildbicubic(&y, &x, &ft, n, m, &c2, _state);
                    }
                    
                    /*
                     * Test
                     */
                    for(i=1; i<=10; i++)
                    {
                        t1 = ax+(bx-ax)*ae_randomreal(_state);
                        t2 = ay+(by-ay)*ae_randomreal(_state);
                        err = ae_maxreal(err, ae_fabs(spline2dcalc(&c, t1, t2, _state)-spline2dcalc(&c2, t2, t1, _state), _state), _state);
                    }
                }
                syerrors = syerrors||ae_fp_greater(err,10000*ae_machineepsilon);
            }
        }
    }
    
    /*
     * Test resample
     */
    for(m=2; m<=6; m++)
    {
        for(n=2; n<=6; n++)
        {
            ae_matrix_set_length(&f, m-1+1, n-1+1, _state);
            ae_vector_set_length(&x, n-1+1, _state);
            ae_vector_set_length(&y, m-1+1, _state);
            for(j=0; j<=n-1; j++)
            {
                x.ptr.p_double[j] = (double)j/(double)(n-1);
            }
            for(i=0; i<=m-1; i++)
            {
                y.ptr.p_double[i] = (double)i/(double)(m-1);
            }
            for(i=0; i<=m-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    f.ptr.pp_double[i][j] = ae_exp(0.6*x.ptr.p_double[j], _state)-ae_exp(-0.3*y.ptr.p_double[i]+0.08*x.ptr.p_double[j], _state)+2*ae_cos(ae_pi*(x.ptr.p_double[j]+1.2*y.ptr.p_double[i]), _state)+0.1*ae_cos(20*x.ptr.p_double[j]+15*y.ptr.p_double[i], _state);
                }
            }
            for(m2=2; m2<=6; m2++)
            {
                for(n2=2; n2<=6; n2++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        for(jobtype=0; jobtype<=1; jobtype++)
                        {
                            if( jobtype==0 )
                            {
                                spline2dresamplebilinear(&f, m, n, &fr, m2, n2, _state);
                                spline2dbuildbilinear(&x, &y, &f, m, n, &c, _state);
                            }
                            if( jobtype==1 )
                            {
                                spline2dresamplebicubic(&f, m, n, &fr, m2, n2, _state);
                                spline2dbuildbicubic(&x, &y, &f, m, n, &c, _state);
                            }
                            err = 0;
                            mf = 0;
                            for(i=0; i<=m2-1; i++)
                            {
                                for(j=0; j<=n2-1; j++)
                                {
                                    v1 = spline2dcalc(&c, (double)j/(double)(n2-1), (double)i/(double)(m2-1), _state);
                                    v2 = fr.ptr.pp_double[i][j];
                                    err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                                    mf = ae_maxreal(mf, ae_fabs(v1, _state), _state);
                                }
                            }
                            if( jobtype==0 )
                            {
                                rlerrors = rlerrors||ae_fp_greater(err/mf,10000*ae_machineepsilon);
                            }
                            if( jobtype==1 )
                            {
                                rcerrors = rcerrors||ae_fp_greater(err/mf,10000*ae_machineepsilon);
                            }
                        }
                    }
                }
            }
        }
    }
    
    /*
     * report
     */
    waserrors = (((((((blerrors||bcerrors)||dserrors)||cperrors)||uperrors)||lterrors)||syerrors)||rlerrors)||rcerrors;
    if( !silent )
    {
        printf("TESTING 2D INTERPOLATION\n");
        
        /*
         * Normal tests
         */
        printf("BILINEAR TEST:                           ");
        if( blerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("BICUBIC TEST:                            ");
        if( bcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("DIFFERENTIATION TEST:                    ");
        if( dserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("COPY/SERIALIZE TEST:                     ");
        if( cperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("UNPACK TEST:                             ");
        if( uperrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("LIN.TRANS. TEST:                         ");
        if( lterrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("SPECIAL SYMMETRY TEST:                   ");
        if( syerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("BILINEAR RESAMPLING TEST:                ");
        if( rlerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("BICUBIC RESAMPLING TEST:                 ");
        if( rcerrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        
        /*
         * Summary
         */
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    
    /*
     * end
     */
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Lipschitz constants for spline inself, first and second derivatives.
*************************************************************************/
static void testspline2dunit_lconst(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_int_t m,
     ae_int_t n,
     double lstep,
     double* lc,
     double* lcx,
     double* lcy,
     double* lcxy,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double f1;
    double f2;
    double f3;
    double f4;
    double fx1;
    double fx2;
    double fx3;
    double fx4;
    double fy1;
    double fy2;
    double fy3;
    double fy4;
    double fxy1;
    double fxy2;
    double fxy3;
    double fxy4;
    double s2lstep;

    *lc = 0;
    *lcx = 0;
    *lcy = 0;
    *lcxy = 0;

    *lc = 0;
    *lcx = 0;
    *lcy = 0;
    *lcxy = 0;
    s2lstep = ae_sqrt(2, _state)*lstep;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            
            /*
             * Calculate
             */
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]-lstep/2, ly->ptr.p_double[i]-lstep/2, lstep/4, &f1, &fx1, &fy1, &fxy1, _state);
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]+lstep/2, ly->ptr.p_double[i]-lstep/2, lstep/4, &f2, &fx2, &fy2, &fxy2, _state);
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]+lstep/2, ly->ptr.p_double[i]+lstep/2, lstep/4, &f3, &fx3, &fy3, &fxy3, _state);
            testspline2dunit_twodnumder(c, lx->ptr.p_double[j]-lstep/2, ly->ptr.p_double[i]+lstep/2, lstep/4, &f4, &fx4, &fy4, &fxy4, _state);
            
            /*
             * Lipschitz constant for the function itself
             */
            *lc = ae_maxreal(*lc, ae_fabs((f1-f2)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f2-f3)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f3-f4)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f4-f1)/lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f1-f3)/s2lstep, _state), _state);
            *lc = ae_maxreal(*lc, ae_fabs((f2-f4)/s2lstep, _state), _state);
            
            /*
             * Lipschitz constant for the first derivative
             */
            *lcx = ae_maxreal(*lcx, ae_fabs((fx1-fx2)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx2-fx3)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx3-fx4)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx4-fx1)/lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx1-fx3)/s2lstep, _state), _state);
            *lcx = ae_maxreal(*lcx, ae_fabs((fx2-fx4)/s2lstep, _state), _state);
            
            /*
             * Lipschitz constant for the first derivative
             */
            *lcy = ae_maxreal(*lcy, ae_fabs((fy1-fy2)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy2-fy3)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy3-fy4)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy4-fy1)/lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy1-fy3)/s2lstep, _state), _state);
            *lcy = ae_maxreal(*lcy, ae_fabs((fy2-fy4)/s2lstep, _state), _state);
            
            /*
             * Lipschitz constant for the cross-derivative
             */
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy1-fxy2)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy2-fxy3)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy3-fxy4)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy4-fxy1)/lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy1-fxy3)/s2lstep, _state), _state);
            *lcxy = ae_maxreal(*lcxy, ae_fabs((fxy2-fxy4)/s2lstep, _state), _state);
        }
    }
}


/*************************************************************************
Numerical differentiation.
*************************************************************************/
static void testspline2dunit_twodnumder(spline2dinterpolant* c,
     double x,
     double y,
     double h,
     double* f,
     double* fx,
     double* fy,
     double* fxy,
     ae_state *_state)
{

    *f = 0;
    *fx = 0;
    *fy = 0;
    *fxy = 0;

    *f = spline2dcalc(c, x, y, _state);
    *fx = (spline2dcalc(c, x+h, y, _state)-spline2dcalc(c, x-h, y, _state))/(2*h);
    *fy = (spline2dcalc(c, x, y+h, _state)-spline2dcalc(c, x, y-h, _state))/(2*h);
    *fxy = (spline2dcalc(c, x+h, y+h, _state)-spline2dcalc(c, x-h, y+h, _state)-spline2dcalc(c, x+h, y-h, _state)+spline2dcalc(c, x-h, y-h, _state))/ae_sqr(2*h, _state);
}


/*************************************************************************
Unpack test
*************************************************************************/
static ae_bool testspline2dunit_testunpack(spline2dinterpolant* c,
     /* Real    */ ae_vector* lx,
     /* Real    */ ae_vector* ly,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t n;
    ae_int_t m;
    ae_int_t ci;
    ae_int_t cj;
    ae_int_t p;
    double err;
    double tx;
    double ty;
    double v1;
    double v2;
    ae_int_t pass;
    ae_int_t passcount;
    ae_matrix tbl;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&tbl, 0, 0, DT_REAL, _state, ae_true);

    passcount = 20;
    err = 0;
    spline2dunpack(c, &m, &n, &tbl, _state);
    for(i=0; i<=m-2; i++)
    {
        for(j=0; j<=n-2; j++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                p = (n-1)*i+j;
                tx = (0.001+0.999*ae_randomreal(_state))*(tbl.ptr.pp_double[p][1]-tbl.ptr.pp_double[p][0]);
                ty = (0.001+0.999*ae_randomreal(_state))*(tbl.ptr.pp_double[p][3]-tbl.ptr.pp_double[p][2]);
                
                /*
                 * Interpolation properties
                 */
                v1 = 0;
                for(ci=0; ci<=3; ci++)
                {
                    for(cj=0; cj<=3; cj++)
                    {
                        v1 = v1+tbl.ptr.pp_double[p][4+ci*4+cj]*ae_pow(tx, ci, _state)*ae_pow(ty, cj, _state);
                    }
                }
                v2 = spline2dcalc(c, tbl.ptr.pp_double[p][0]+tx, tbl.ptr.pp_double[p][2]+ty, _state);
                err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                
                /*
                 * Grid correctness
                 */
                err = ae_maxreal(err, ae_fabs(lx->ptr.p_double[2*j]-tbl.ptr.pp_double[p][0], _state), _state);
                err = ae_maxreal(err, ae_fabs(lx->ptr.p_double[2*(j+1)]-tbl.ptr.pp_double[p][1], _state), _state);
                err = ae_maxreal(err, ae_fabs(ly->ptr.p_double[2*i]-tbl.ptr.pp_double[p][2], _state), _state);
                err = ae_maxreal(err, ae_fabs(ly->ptr.p_double[2*(i+1)]-tbl.ptr.pp_double[p][3], _state), _state);
            }
        }
    }
    result = ae_fp_less(err,10000*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LinTrans test
*************************************************************************/
static ae_bool testspline2dunit_testlintrans(spline2dinterpolant* c,
     double ax,
     double bx,
     double ay,
     double by,
     ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double a1;
    double a2;
    double b1;
    double b2;
    double tx;
    double ty;
    double vx;
    double vy;
    double v1;
    double v2;
    ae_int_t pass;
    ae_int_t passcount;
    ae_int_t xjob;
    ae_int_t yjob;
    spline2dinterpolant c2;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    _spline2dinterpolant_init(&c2, _state, ae_true);

    passcount = 5;
    err = 0;
    for(xjob=0; xjob<=1; xjob++)
    {
        for(yjob=0; yjob<=1; yjob++)
        {
            for(pass=1; pass<=passcount; pass++)
            {
                
                /*
                 * Prepare
                 */
                do
                {
                    a1 = 2*ae_randomreal(_state)-1;
                }
                while(ae_fp_eq(a1,0));
                a1 = a1*xjob;
                b1 = 2*ae_randomreal(_state)-1;
                do
                {
                    a2 = 2*ae_randomreal(_state)-1;
                }
                while(ae_fp_eq(a2,0));
                a2 = a2*yjob;
                b2 = 2*ae_randomreal(_state)-1;
                
                /*
                 * Test XY
                 */
                spline2dcopy(c, &c2, _state);
                spline2dlintransxy(&c2, a1, b1, a2, b2, _state);
                tx = ax+ae_randomreal(_state)*(bx-ax);
                ty = ay+ae_randomreal(_state)*(by-ay);
                if( xjob==0 )
                {
                    tx = b1;
                    vx = ax+ae_randomreal(_state)*(bx-ax);
                }
                else
                {
                    vx = (tx-b1)/a1;
                }
                if( yjob==0 )
                {
                    ty = b2;
                    vy = ay+ae_randomreal(_state)*(by-ay);
                }
                else
                {
                    vy = (ty-b2)/a2;
                }
                v1 = spline2dcalc(c, tx, ty, _state);
                v2 = spline2dcalc(&c2, vx, vy, _state);
                err = ae_maxreal(err, ae_fabs(v1-v2, _state), _state);
                
                /*
                 * Test F
                 */
                spline2dcopy(c, &c2, _state);
                spline2dlintransf(&c2, a1, b1, _state);
                tx = ax+ae_randomreal(_state)*(bx-ax);
                ty = ay+ae_randomreal(_state)*(by-ay);
                v1 = spline2dcalc(c, tx, ty, _state);
                v2 = spline2dcalc(&c2, tx, ty, _state);
                err = ae_maxreal(err, ae_fabs(a1*v1+b1-v2, _state), _state);
            }
        }
    }
    result = ae_fp_less(err,10000*ae_machineepsilon);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Unset spline, i.e. initialize it with random garbage
*************************************************************************/
static void testspline2dunit_unsetspline2d(spline2dinterpolant* c,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector x;
    ae_vector y;
    ae_matrix f;

    ae_frame_make(_state, &_frame_block);
    _spline2dinterpolant_clear(c);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&f, 0, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&y, 2, _state);
    ae_matrix_set_length(&f, 2, 2, _state);
    x.ptr.p_double[0] = -1;
    x.ptr.p_double[1] = 1;
    y.ptr.p_double[0] = -1;
    y.ptr.p_double[1] = 1;
    f.ptr.pp_double[0][0] = 0;
    f.ptr.pp_double[0][1] = 0;
    f.ptr.pp_double[1][0] = 0;
    f.ptr.pp_double[1][1] = 0;
    spline2dbuildbilinear(&x, &y, &f, 2, 2, c, _state);
    ae_frame_leave(_state);
}








/*************************************************************************
Testing bidiagonal SVD decomposition subroutine
*************************************************************************/
ae_bool testspdgevd(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t pass;
    ae_int_t n;
    ae_int_t passcount;
    ae_int_t maxn;
    ae_int_t atask;
    ae_int_t btask;
    ae_vector d;
    ae_vector t1;
    ae_matrix a;
    ae_matrix b;
    ae_matrix afull;
    ae_matrix bfull;
    ae_matrix l;
    ae_matrix z;
    ae_bool isuppera;
    ae_bool isupperb;
    ae_int_t i;
    ae_int_t j;
    ae_int_t minij;
    double v;
    double v1;
    double v2;
    double err;
    double valerr;
    double threshold;
    ae_bool waserrors;
    ae_bool wfailed;
    ae_bool wnsorted;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&t1, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&afull, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&bfull, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&l, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);

    threshold = 10000*ae_machineepsilon;
    valerr = 0;
    wfailed = ae_false;
    wnsorted = ae_false;
    maxn = 20;
    passcount = 5;
    
    /*
     * Main cycle
     */
    for(n=1; n<=maxn; n++)
    {
        for(pass=1; pass<=passcount; pass++)
        {
            for(atask=0; atask<=1; atask++)
            {
                for(btask=0; btask<=1; btask++)
                {
                    isuppera = atask==0;
                    isupperb = btask==0;
                    
                    /*
                     * Initialize A, B, AFull, BFull
                     */
                    ae_vector_set_length(&t1, n-1+1, _state);
                    ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&b, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&afull, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&bfull, n-1+1, n-1+1, _state);
                    ae_matrix_set_length(&l, n-1+1, n-1+1, _state);
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                            a.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                            afull.ptr.pp_double[i][j] = a.ptr.pp_double[i][j];
                            afull.ptr.pp_double[j][i] = a.ptr.pp_double[i][j];
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            l.ptr.pp_double[i][j] = ae_randomreal(_state);
                            l.ptr.pp_double[j][i] = l.ptr.pp_double[i][j];
                        }
                        l.ptr.pp_double[i][i] = 1.5+ae_randomreal(_state);
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            minij = ae_minint(i, j, _state);
                            v = ae_v_dotproduct(&l.ptr.pp_double[i][0], 1, &l.ptr.pp_double[0][j], l.stride, ae_v_len(0,minij));
                            b.ptr.pp_double[i][j] = v;
                            b.ptr.pp_double[j][i] = v;
                            bfull.ptr.pp_double[i][j] = v;
                            bfull.ptr.pp_double[j][i] = v;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=n-1; j++)
                        {
                            if( isuppera )
                            {
                                if( j<i )
                                {
                                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                            else
                            {
                                if( i<j )
                                {
                                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                            if( isupperb )
                            {
                                if( j<i )
                                {
                                    b.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                            else
                            {
                                if( i<j )
                                {
                                    b.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                                }
                            }
                        }
                    }
                    
                    /*
                     * Problem 1
                     */
                    if( !smatrixgevd(&a, n, isuppera, &b, isupperb, 1, 1, &d, &z, _state) )
                    {
                        wfailed = ae_true;
                        continue;
                    }
                    err = 0;
                    for(j=0; j<=n-1; j++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = ae_v_dotproduct(&afull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            v2 = ae_v_dotproduct(&bfull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            err = ae_maxreal(err, ae_fabs(v1-d.ptr.p_double[j]*v2, _state), _state);
                        }
                    }
                    valerr = ae_maxreal(err, valerr, _state);
                    
                    /*
                     * Problem 2
                     */
                    if( !smatrixgevd(&a, n, isuppera, &b, isupperb, 1, 2, &d, &z, _state) )
                    {
                        wfailed = ae_true;
                        continue;
                    }
                    err = 0;
                    for(j=0; j<=n-1; j++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = ae_v_dotproduct(&bfull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            t1.ptr.p_double[i] = v1;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v2 = ae_v_dotproduct(&afull.ptr.pp_double[i][0], 1, &t1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            err = ae_maxreal(err, ae_fabs(v2-d.ptr.p_double[j]*z.ptr.pp_double[i][j], _state), _state);
                        }
                    }
                    valerr = ae_maxreal(err, valerr, _state);
                    
                    /*
                     * Test problem 3
                     */
                    if( !smatrixgevd(&a, n, isuppera, &b, isupperb, 1, 3, &d, &z, _state) )
                    {
                        wfailed = ae_true;
                        continue;
                    }
                    err = 0;
                    for(j=0; j<=n-1; j++)
                    {
                        for(i=0; i<=n-1; i++)
                        {
                            v1 = ae_v_dotproduct(&afull.ptr.pp_double[i][0], 1, &z.ptr.pp_double[0][j], z.stride, ae_v_len(0,n-1));
                            t1.ptr.p_double[i] = v1;
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            v2 = ae_v_dotproduct(&bfull.ptr.pp_double[i][0], 1, &t1.ptr.p_double[0], 1, ae_v_len(0,n-1));
                            err = ae_maxreal(err, ae_fabs(v2-d.ptr.p_double[j]*z.ptr.pp_double[i][j], _state), _state);
                        }
                    }
                    valerr = ae_maxreal(err, valerr, _state);
                }
            }
        }
    }
    
    /*
     * report
     */
    waserrors = (ae_fp_greater(valerr,threshold)||wfailed)||wnsorted;
    if( !silent )
    {
        printf("TESTING SYMMETRIC GEVD\n");
        printf("Av-lambdav error (generalized):          %5.3e\n",
            (double)(valerr));
        printf("Eigen values order:                      ");
        if( !wnsorted )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static void testinverseupdateunit_makeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state);
static void testinverseupdateunit_matlu(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Integer */ ae_vector* pivots,
     ae_state *_state);
static void testinverseupdateunit_generaterandomorthogonalmatrix(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state);
static void testinverseupdateunit_generaterandommatrixcond(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     double c,
     ae_state *_state);
static ae_bool testinverseupdateunit_invmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state);
static ae_bool testinverseupdateunit_invmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state);
static ae_bool testinverseupdateunit_invmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state);
static double testinverseupdateunit_matrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state);
static ae_bool testinverseupdateunit_updandinv(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* u,
     /* Real    */ ae_vector* v,
     ae_int_t n,
     ae_state *_state);





ae_bool testinverseupdate(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_matrix inva;
    ae_matrix b1;
    ae_matrix b2;
    ae_vector u;
    ae_vector v;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t updrow;
    ae_int_t updcol;
    double val;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    double threshold;
    double c;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&inva, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&b2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&u, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v, 0, DT_REAL, _state, ae_true);

    waserrors = ae_false;
    maxn = 10;
    passcount = 100;
    threshold = 1.0E-6;
    
    /*
     * process
     */
    for(n=1; n<=maxn; n++)
    {
        ae_matrix_set_length(&a, n-1+1, n-1+1, _state);
        ae_matrix_set_length(&b1, n-1+1, n-1+1, _state);
        ae_matrix_set_length(&b2, n-1+1, n-1+1, _state);
        ae_vector_set_length(&u, n-1+1, _state);
        ae_vector_set_length(&v, n-1+1, _state);
        for(pass=1; pass<=passcount; pass++)
        {
            c = ae_exp(ae_randomreal(_state)*ae_log(10, _state), _state);
            testinverseupdateunit_generaterandommatrixcond(&a, n, c, _state);
            testinverseupdateunit_makeacopy(&a, n, n, &inva, _state);
            if( !testinverseupdateunit_invmat(&inva, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            
            /*
             * Test simple update
             */
            updrow = ae_randominteger(n, _state);
            updcol = ae_randominteger(n, _state);
            val = 0.1*(2*ae_randomreal(_state)-1);
            for(i=0; i<=n-1; i++)
            {
                if( i==updrow )
                {
                    u.ptr.p_double[i] = val;
                }
                else
                {
                    u.ptr.p_double[i] = 0;
                }
                if( i==updcol )
                {
                    v.ptr.p_double[i] = 1;
                }
                else
                {
                    v.ptr.p_double[i] = 0;
                }
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdatesimple(&b2, n, updrow, updcol, val, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
            
            /*
             * Test row update
             */
            updrow = ae_randominteger(n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( i==updrow )
                {
                    u.ptr.p_double[i] = 1;
                }
                else
                {
                    u.ptr.p_double[i] = 0;
                }
                v.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdaterow(&b2, n, updrow, &v, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
            
            /*
             * Test column update
             */
            updcol = ae_randominteger(n, _state);
            for(i=0; i<=n-1; i++)
            {
                if( i==updcol )
                {
                    v.ptr.p_double[i] = 1;
                }
                else
                {
                    v.ptr.p_double[i] = 0;
                }
                u.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdatecolumn(&b2, n, updcol, &u, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
            
            /*
             * Test full update
             */
            for(i=0; i<=n-1; i++)
            {
                v.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
                u.ptr.p_double[i] = 0.1*(2*ae_randomreal(_state)-1);
            }
            testinverseupdateunit_makeacopy(&a, n, n, &b1, _state);
            if( !testinverseupdateunit_updandinv(&b1, &u, &v, n, _state) )
            {
                waserrors = ae_true;
                break;
            }
            testinverseupdateunit_makeacopy(&inva, n, n, &b2, _state);
            rmatrixinvupdateuv(&b2, n, &u, &v, _state);
            waserrors = waserrors||ae_fp_greater(testinverseupdateunit_matrixdiff(&b1, &b2, n, n, _state),threshold);
        }
    }
    
    /*
     * report
     */
    if( !silent )
    {
        printf("TESTING INVERSE UPDATE (REAL)\n");
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Copy
*************************************************************************/
static void testinverseupdateunit_makeacopy(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Real    */ ae_matrix* b,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;

    ae_matrix_clear(b);

    ae_matrix_set_length(b, m-1+1, n-1+1, _state);
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            b->ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
}


/*************************************************************************
LU decomposition
*************************************************************************/
static void testinverseupdateunit_matlu(/* Real    */ ae_matrix* a,
     ae_int_t m,
     ae_int_t n,
     /* Integer */ ae_vector* pivots,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    ae_vector t1;
    double s;

    ae_frame_make(_state, &_frame_block);
    ae_vector_clear(pivots);
    ae_vector_init(&t1, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(pivots, ae_minint(m-1, n-1, _state)+1, _state);
    ae_vector_set_length(&t1, ae_maxint(m-1, n-1, _state)+1, _state);
    ae_assert(m>=0&&n>=0, "Error in LUDecomposition: incorrect function arguments", _state);
    
    /*
     * Quick return if possible
     */
    if( m==0||n==0 )
    {
        ae_frame_leave(_state);
        return;
    }
    for(j=0; j<=ae_minint(m-1, n-1, _state); j++)
    {
        
        /*
         * Find pivot and test for singularity.
         */
        jp = j;
        for(i=j+1; i<=m-1; i++)
        {
            if( ae_fp_greater(ae_fabs(a->ptr.pp_double[i][j], _state),ae_fabs(a->ptr.pp_double[jp][j], _state)) )
            {
                jp = i;
            }
        }
        pivots->ptr.p_int[j] = jp;
        if( ae_fp_neq(a->ptr.pp_double[jp][j],0) )
        {
            
            /*
             *Apply the interchange to rows
             */
            if( jp!=j )
            {
                ae_v_move(&t1.ptr.p_double[0], 1, &a->ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
                ae_v_move(&a->ptr.pp_double[j][0], 1, &a->ptr.pp_double[jp][0], 1, ae_v_len(0,n-1));
                ae_v_move(&a->ptr.pp_double[jp][0], 1, &t1.ptr.p_double[0], 1, ae_v_len(0,n-1));
            }
            
            /*
             *Compute elements J+1:M of J-th column.
             */
            if( j<m )
            {
                jp = j+1;
                s = 1/a->ptr.pp_double[j][j];
                ae_v_muld(&a->ptr.pp_double[jp][j], a->stride, ae_v_len(jp,m-1), s);
            }
        }
        if( j<ae_minint(m, n, _state)-1 )
        {
            
            /*
             *Update trailing submatrix.
             */
            jp = j+1;
            for(i=j+1; i<=m-1; i++)
            {
                s = a->ptr.pp_double[i][j];
                ae_v_subd(&a->ptr.pp_double[i][jp], 1, &a->ptr.pp_double[j][jp], 1, ae_v_len(jp,n-1), s);
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
Generate matrix with given condition number C (2-norm)
*************************************************************************/
static void testinverseupdateunit_generaterandomorthogonalmatrix(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    double t;
    double lambdav;
    ae_int_t s;
    ae_int_t i;
    ae_int_t j;
    double u1;
    double u2;
    ae_vector w;
    ae_vector v;
    ae_matrix a;
    double sm;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&v, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    if( n<=0 )
    {
        ae_frame_leave(_state);
        return;
    }
    ae_vector_set_length(&w, n+1, _state);
    ae_vector_set_length(&v, n+1, _state);
    ae_matrix_set_length(&a, n+1, n+1, _state);
    ae_matrix_set_length(a0, n-1+1, n-1+1, _state);
    
    /*
     * Prepare A
     */
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            if( i==j )
            {
                a.ptr.pp_double[i][j] = 1;
            }
            else
            {
                a.ptr.pp_double[i][j] = 0;
            }
        }
    }
    
    /*
     * Calculate A using Stewart algorithm
     */
    for(s=2; s<=n; s++)
    {
        
        /*
         * Prepare v and Lambda = v'*v
         */
        do
        {
            i = 1;
            while(i<=s)
            {
                u1 = 2*ae_randomreal(_state)-1;
                u2 = 2*ae_randomreal(_state)-1;
                sm = u1*u1+u2*u2;
                if( ae_fp_eq(sm,0)||ae_fp_greater(sm,1) )
                {
                    continue;
                }
                sm = ae_sqrt(-2*ae_log(sm, _state)/sm, _state);
                v.ptr.p_double[i] = u1*sm;
                if( i+1<=s )
                {
                    v.ptr.p_double[i+1] = u2*sm;
                }
                i = i+2;
            }
            lambdav = ae_v_dotproduct(&v.ptr.p_double[1], 1, &v.ptr.p_double[1], 1, ae_v_len(1,s));
        }
        while(ae_fp_eq(lambdav,0));
        lambdav = 2/lambdav;
        
        /*
         * A * (I - 2 vv'/v'v ) =
         *   = A - (2/v'v) * A * v * v' =
         *   = A - (2/v'v) * w * v'
         *  where w = Av
         */
        for(i=1; i<=s; i++)
        {
            t = ae_v_dotproduct(&a.ptr.pp_double[i][1], 1, &v.ptr.p_double[1], 1, ae_v_len(1,s));
            w.ptr.p_double[i] = t;
        }
        for(i=1; i<=s; i++)
        {
            t = w.ptr.p_double[i]*lambdav;
            ae_v_subd(&a.ptr.pp_double[i][1], 1, &v.ptr.p_double[1], 1, ae_v_len(1,s), t);
        }
    }
    
    /*
     *
     */
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=n; j++)
        {
            a0->ptr.pp_double[i-1][j-1] = a.ptr.pp_double[i][j];
        }
    }
    ae_frame_leave(_state);
}


static void testinverseupdateunit_generaterandommatrixcond(/* Real    */ ae_matrix* a0,
     ae_int_t n,
     double c,
     ae_state *_state)
{
    ae_frame _frame_block;
    double l1;
    double l2;
    ae_matrix q1;
    ae_matrix q2;
    ae_vector cc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&q1, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&q2, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&cc, 0, DT_REAL, _state, ae_true);

    testinverseupdateunit_generaterandomorthogonalmatrix(&q1, n, _state);
    testinverseupdateunit_generaterandomorthogonalmatrix(&q2, n, _state);
    ae_vector_set_length(&cc, n-1+1, _state);
    l1 = 0;
    l2 = ae_log(1/c, _state);
    cc.ptr.p_double[0] = ae_exp(l1, _state);
    for(i=1; i<=n-2; i++)
    {
        cc.ptr.p_double[i] = ae_exp(ae_randomreal(_state)*(l2-l1)+l1, _state);
    }
    cc.ptr.p_double[n-1] = ae_exp(l2, _state);
    ae_matrix_set_length(a0, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            a0->ptr.pp_double[i][j] = 0;
            for(k=0; k<=n-1; k++)
            {
                a0->ptr.pp_double[i][j] = a0->ptr.pp_double[i][j]+q1.ptr.pp_double[i][k]*cc.ptr.p_double[k]*q2.ptr.pp_double[j][k];
            }
        }
    }
    ae_frame_leave(_state);
}


/*************************************************************************
triangular inverse
*************************************************************************/
static ae_bool testinverseupdateunit_invmattr(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_bool isupper,
     ae_bool isunittriangular,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool nounit;
    ae_int_t i;
    ae_int_t j;
    double v;
    double ajj;
    ae_vector t;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&t, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    ae_vector_set_length(&t, n-1+1, _state);
    
    /*
     * Test the input parameters.
     */
    nounit = !isunittriangular;
    if( isupper )
    {
        
        /*
         * Compute inverse of upper triangular matrix.
         */
        for(j=0; j<=n-1; j++)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = -1;
            }
            
            /*
             * Compute elements 1:j-1 of j-th column.
             */
            if( j>0 )
            {
                ae_v_move(&t.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1));
                for(i=0; i<=j-1; i++)
                {
                    if( i<j-1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][i+1], 1, &t.ptr.p_double[i+1], 1, ae_v_len(i+1,j-1));
                    }
                    else
                    {
                        v = 0;
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[0][j], a->stride, ae_v_len(0,j-1), ajj);
            }
        }
    }
    else
    {
        
        /*
         * Compute inverse of lower triangular matrix.
         */
        for(j=n-1; j>=0; j--)
        {
            if( nounit )
            {
                if( ae_fp_eq(a->ptr.pp_double[j][j],0) )
                {
                    result = ae_false;
                    ae_frame_leave(_state);
                    return result;
                }
                a->ptr.pp_double[j][j] = 1/a->ptr.pp_double[j][j];
                ajj = -a->ptr.pp_double[j][j];
            }
            else
            {
                ajj = -1;
            }
            if( j<n-1 )
            {
                
                /*
                 * Compute elements j+1:n of j-th column.
                 */
                ae_v_move(&t.ptr.p_double[j+1], 1, &a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1));
                for(i=j+1; i<=n-1; i++)
                {
                    if( i>j+1 )
                    {
                        v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &t.ptr.p_double[j+1], 1, ae_v_len(j+1,i-1));
                    }
                    else
                    {
                        v = 0;
                    }
                    if( nounit )
                    {
                        a->ptr.pp_double[i][j] = v+a->ptr.pp_double[i][i]*t.ptr.p_double[i];
                    }
                    else
                    {
                        a->ptr.pp_double[i][j] = v+t.ptr.p_double[i];
                    }
                }
                ae_v_muld(&a->ptr.pp_double[j+1][j], a->stride, ae_v_len(j+1,n-1), ajj);
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
LU inverse
*************************************************************************/
static ae_bool testinverseupdateunit_invmatlu(/* Real    */ ae_matrix* a,
     /* Integer */ ae_vector* pivots,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector work;
    ae_int_t i;
    ae_int_t j;
    ae_int_t jp;
    double v;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&work, 0, DT_REAL, _state, ae_true);

    result = ae_true;
    
    /*
     * Quick return if possible
     */
    if( n==0 )
    {
        ae_frame_leave(_state);
        return result;
    }
    ae_vector_set_length(&work, n-1+1, _state);
    
    /*
     * Form inv(U)
     */
    if( !testinverseupdateunit_invmattr(a, n, ae_true, ae_false, _state) )
    {
        result = ae_false;
        ae_frame_leave(_state);
        return result;
    }
    
    /*
     * Solve the equation inv(A)*L = inv(U) for inv(A).
     */
    for(j=n-1; j>=0; j--)
    {
        
        /*
         * Copy current column of L to WORK and replace with zeros.
         */
        for(i=j+1; i<=n-1; i++)
        {
            work.ptr.p_double[i] = a->ptr.pp_double[i][j];
            a->ptr.pp_double[i][j] = 0;
        }
        
        /*
         * Compute current column of inv(A).
         */
        if( j<n-1 )
        {
            for(i=0; i<=n-1; i++)
            {
                v = ae_v_dotproduct(&a->ptr.pp_double[i][j+1], 1, &work.ptr.p_double[j+1], 1, ae_v_len(j+1,n-1));
                a->ptr.pp_double[i][j] = a->ptr.pp_double[i][j]-v;
            }
        }
    }
    
    /*
     * Apply column interchanges.
     */
    for(j=n-2; j>=0; j--)
    {
        jp = pivots->ptr.p_int[j];
        if( jp!=j )
        {
            ae_v_move(&work.ptr.p_double[0], 1, &a->ptr.pp_double[0][j], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][j], a->stride, &a->ptr.pp_double[0][jp], a->stride, ae_v_len(0,n-1));
            ae_v_move(&a->ptr.pp_double[0][jp], a->stride, &work.ptr.p_double[0], 1, ae_v_len(0,n-1));
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Matrix inverse
*************************************************************************/
static ae_bool testinverseupdateunit_invmat(/* Real    */ ae_matrix* a,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    testinverseupdateunit_matlu(a, n, n, &pivots, _state);
    result = testinverseupdateunit_invmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Diff
*************************************************************************/
static double testinverseupdateunit_matrixdiff(/* Real    */ ae_matrix* a,
     /* Real    */ ae_matrix* b,
     ae_int_t m,
     ae_int_t n,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;
    double result;


    result = 0;
    for(i=0; i<=m-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            result = ae_maxreal(result, ae_fabs(b->ptr.pp_double[i][j]-a->ptr.pp_double[i][j], _state), _state);
        }
    }
    return result;
}


/*************************************************************************
Update and inverse
*************************************************************************/
static ae_bool testinverseupdateunit_updandinv(/* Real    */ ae_matrix* a,
     /* Real    */ ae_vector* u,
     /* Real    */ ae_vector* v,
     ae_int_t n,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_vector pivots;
    ae_int_t i;
    double r;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&pivots, 0, DT_INT, _state, ae_true);

    for(i=0; i<=n-1; i++)
    {
        r = u->ptr.p_double[i];
        ae_v_addd(&a->ptr.pp_double[i][0], 1, &v->ptr.p_double[0], 1, ae_v_len(0,n-1), r);
    }
    testinverseupdateunit_matlu(a, n, n, &pivots, _state);
    result = testinverseupdateunit_invmatlu(a, &pivots, n, _state);
    ae_frame_leave(_state);
    return result;
}



static void testschurunit_fillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double sparcity,
     ae_state *_state);
static void testschurunit_testschurproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* errstruct,
     ae_bool* wfailed,
     ae_state *_state);





/*************************************************************************
Testing Schur decomposition subroutine
*************************************************************************/
ae_bool testschur(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix a;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t j;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool waserrors;
    ae_bool errstruct;
    ae_bool wfailed;
    double materr;
    double orterr;
    double threshold;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    materr = 0;
    orterr = 0;
    errstruct = ae_false;
    wfailed = ae_false;
    waserrors = ae_false;
    maxn = 70;
    passcount = 1;
    threshold = 5*100*ae_machineepsilon;
    ae_matrix_set_length(&a, maxn-1+1, maxn-1+1, _state);
    
    /*
     * zero matrix, several cases
     */
    for(i=0; i<=maxn-1; i++)
    {
        for(j=0; j<=maxn-1; j++)
        {
            a.ptr.pp_double[i][j] = 0;
        }
    }
    for(n=1; n<=maxn; n++)
    {
        if( n>30&&n%2==0 )
        {
            continue;
        }
        testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
    }
    
    /*
     * Dense matrix
     */
    for(pass=1; pass<=passcount; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            if( n>30&&n%2==0 )
            {
                continue;
            }
            for(i=0; i<=n-1; i++)
            {
                for(j=0; j<=n-1; j++)
                {
                    a.ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
                }
            }
            testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
        }
    }
    
    /*
     * Sparse matrices, very sparse matrices, incredible sparse matrices
     */
    for(pass=1; pass<=1; pass++)
    {
        for(n=1; n<=maxn; n++)
        {
            if( n>30&&n%3!=0 )
            {
                continue;
            }
            testschurunit_fillsparsea(&a, n, 0.8, _state);
            testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
            testschurunit_fillsparsea(&a, n, 0.9, _state);
            testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
            testschurunit_fillsparsea(&a, n, 0.95, _state);
            testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
            testschurunit_fillsparsea(&a, n, 0.997, _state);
            testschurunit_testschurproblem(&a, n, &materr, &orterr, &errstruct, &wfailed, _state);
        }
    }
    
    /*
     * report
     */
    waserrors = ((ae_fp_greater(materr,threshold)||ae_fp_greater(orterr,threshold))||errstruct)||wfailed;
    if( !silent )
    {
        printf("TESTING SCHUR DECOMPOSITION\n");
        printf("Schur decomposition error:               %5.3e\n",
            (double)(materr));
        printf("Schur orthogonality error:               %5.3e\n",
            (double)(orterr));
        printf("T matrix structure:                      ");
        if( !errstruct )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Always converged:                        ");
        if( !wfailed )
        {
            printf("OK\n");
        }
        else
        {
            printf("FAILED\n");
        }
        printf("Threshold:                               %5.3e\n",
            (double)(threshold));
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


static void testschurunit_fillsparsea(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double sparcity,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t j;


    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            if( ae_fp_greater_eq(ae_randomreal(_state),sparcity) )
            {
                a->ptr.pp_double[i][j] = 2*ae_randomreal(_state)-1;
            }
            else
            {
                a->ptr.pp_double[i][j] = 0;
            }
        }
    }
}


static void testschurunit_testschurproblem(/* Real    */ ae_matrix* a,
     ae_int_t n,
     double* materr,
     double* orterr,
     ae_bool* errstruct,
     ae_bool* wfailed,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_matrix s;
    ae_matrix t;
    ae_vector sr;
    ae_vector astc;
    ae_vector sastc;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    double v;
    double locerr;

    ae_frame_make(_state, &_frame_block);
    ae_matrix_init(&s, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&t, 0, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sr, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&astc, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&sastc, 0, DT_REAL, _state, ae_true);

    ae_vector_set_length(&sr, n-1+1, _state);
    ae_vector_set_length(&astc, n-1+1, _state);
    ae_vector_set_length(&sastc, n-1+1, _state);
    
    /*
     * Schur decomposition, convergence test
     */
    ae_matrix_set_length(&t, n-1+1, n-1+1, _state);
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            t.ptr.pp_double[i][j] = a->ptr.pp_double[i][j];
        }
    }
    if( !rmatrixschur(&t, n, &s, _state) )
    {
        *wfailed = ae_true;
        ae_frame_leave(_state);
        return;
    }
    
    /*
     * decomposition error
     */
    locerr = 0;
    for(j=0; j<=n-1; j++)
    {
        ae_v_move(&sr.ptr.p_double[0], 1, &s.ptr.pp_double[j][0], 1, ae_v_len(0,n-1));
        for(k=0; k<=n-1; k++)
        {
            v = ae_v_dotproduct(&t.ptr.pp_double[k][0], 1, &sr.ptr.p_double[0], 1, ae_v_len(0,n-1));
            astc.ptr.p_double[k] = v;
        }
        for(k=0; k<=n-1; k++)
        {
            v = ae_v_dotproduct(&s.ptr.pp_double[k][0], 1, &astc.ptr.p_double[0], 1, ae_v_len(0,n-1));
            sastc.ptr.p_double[k] = v;
        }
        for(k=0; k<=n-1; k++)
        {
            locerr = ae_maxreal(locerr, ae_fabs(sastc.ptr.p_double[k]-a->ptr.pp_double[k][j], _state), _state);
        }
    }
    *materr = ae_maxreal(*materr, locerr, _state);
    
    /*
     * orthogonality error
     */
    locerr = 0;
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=n-1; j++)
        {
            v = ae_v_dotproduct(&s.ptr.pp_double[0][i], s.stride, &s.ptr.pp_double[0][j], s.stride, ae_v_len(0,n-1));
            if( i!=j )
            {
                locerr = ae_maxreal(locerr, ae_fabs(v, _state), _state);
            }
            else
            {
                locerr = ae_maxreal(locerr, ae_fabs(v-1, _state), _state);
            }
        }
    }
    *orterr = ae_maxreal(*orterr, locerr, _state);
    
    /*
     * T matrix structure
     */
    for(j=0; j<=n-1; j++)
    {
        for(i=j+2; i<=n-1; i++)
        {
            if( ae_fp_neq(t.ptr.pp_double[i][j],0) )
            {
                *errstruct = ae_true;
            }
        }
    }
    ae_frame_leave(_state);
}



static void testnlequnit_testfunchbm(nleqstate* state, ae_state *_state);
static void testnlequnit_testfunchb1(nleqstate* state, ae_state *_state);
static void testnlequnit_testfuncshbm(nleqstate* state, ae_state *_state);





ae_bool testnleq(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool waserrors;
    ae_bool basicserrors;
    ae_bool converror;
    ae_bool othererrors;
    ae_int_t n;
    ae_vector x;
    ae_int_t i;
    ae_int_t k;
    double v;
    double flast;
    ae_vector xlast;
    ae_bool firstrep;
    ae_int_t nfunc;
    ae_int_t njac;
    ae_int_t itcnt;
    nleqstate state;
    nleqreport rep;
    ae_int_t pass;
    ae_int_t passcount;
    double epsf;
    double stpmax;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&xlast, 0, DT_REAL, _state, ae_true);
    _nleqstate_init(&state, _state, ae_true);
    _nleqreport_init(&rep, _state, ae_true);

    waserrors = ae_false;
    basicserrors = ae_false;
    converror = ae_false;
    othererrors = ae_false;
    
    /*
     * Basic tests
     *
     * Test with Himmelblau's function (M):
     * * ability to find correct result
     * * ability to work after soft restart (restart after finish)
     * * ability to work after hard restart (restart in the middle of optimization)
     */
    passcount = 100;
    for(pass=0; pass<=passcount-1; pass++)
    {
        
        /*
         * Ability to find correct result
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 2, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),ae_sqr(epsf, _state));
        }
        else
        {
            basicserrors = ae_true;
        }
        
        /*
         * Ability to work after soft restart
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 2, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqrestartfrom(&state, &x, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),ae_sqr(epsf, _state));
        }
        else
        {
            basicserrors = ae_true;
        }
        
        /*
         * Ability to work after hard restart:
         * * stopping condition: small F
         * * StpMax is so small that we need about 10000 iterations to
         *   find solution (steps are small)
         * * choose random K significantly less that 9999
         * * iterate for some time, then break, restart optimization
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 100;
        x.ptr.p_double[1] = 100;
        nleqcreatelm(2, 2, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        nleqsetstpmax(&state, 0.01, _state);
        k = 1+ae_randominteger(100, _state);
        for(i=0; i<=k-1; i++)
        {
            if( !nleqiteration(&state, _state) )
            {
                break;
            }
            testnlequnit_testfunchbm(&state, _state);
        }
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqrestartfrom(&state, &x, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),ae_sqr(epsf, _state));
        }
        else
        {
            basicserrors = ae_true;
        }
    }
    
    /*
     * Basic tests
     *
     * Test with Himmelblau's function (1):
     * * ability to find correct result
     */
    passcount = 100;
    for(pass=0; pass<=passcount-1; pass++)
    {
        
        /*
         * Ability to find correct result
         */
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 1, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfunchb1(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        if( rep.terminationtype>0 )
        {
            basicserrors = basicserrors||ae_fp_greater(ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state),epsf);
        }
        else
        {
            basicserrors = ae_true;
        }
    }
    
    /*
     * Basic tests
     *
     * Ability to detect situation when we can't find minimum
     */
    passcount = 100;
    for(pass=0; pass<=passcount-1; pass++)
    {
        ae_vector_set_length(&x, 2, _state);
        x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
        x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
        nleqcreatelm(2, 3, &x, &state, _state);
        epsf = 1.0E-9;
        nleqsetcond(&state, epsf, 0, _state);
        while(nleqiteration(&state, _state))
        {
            testnlequnit_testfuncshbm(&state, _state);
        }
        nleqresults(&state, &x, &rep, _state);
        basicserrors = basicserrors||rep.terminationtype!=-4;
    }
    
    /*
     * Test correctness of intermediate reports and final report:
     * * first report is starting point
     * * function value decreases on subsequent reports
     * * function value is correctly reported
     * * last report is final point
     * * NFunc and NJac are compared with values counted directly
     * * IterationsCount is compared with value counter directly
     */
    n = 2;
    ae_vector_set_length(&x, n, _state);
    ae_vector_set_length(&xlast, n, _state);
    x.ptr.p_double[0] = 20*ae_randomreal(_state)-10;
    x.ptr.p_double[1] = 20*ae_randomreal(_state)-10;
    xlast.ptr.p_double[0] = ae_maxrealnumber;
    xlast.ptr.p_double[1] = ae_maxrealnumber;
    nleqcreatelm(n, 2, &x, &state, _state);
    nleqsetcond(&state, 1.0E-6, 0, _state);
    nleqsetxrep(&state, ae_true, _state);
    firstrep = ae_true;
    flast = ae_maxrealnumber;
    nfunc = 0;
    njac = 0;
    itcnt = 0;
    while(nleqiteration(&state, _state))
    {
        if( state.xupdated )
        {
            
            /*
             * first report must be starting point
             */
            if( firstrep )
            {
                for(i=0; i<=n-1; i++)
                {
                    othererrors = othererrors||ae_fp_neq(state.x.ptr.p_double[i],x.ptr.p_double[i]);
                }
                firstrep = ae_false;
            }
            
            /*
             * function value must decrease
             */
            othererrors = othererrors||ae_fp_greater(state.f,flast);
            
            /*
             * check correctness of function value
             */
            v = ae_sqr(state.x.ptr.p_double[0]*state.x.ptr.p_double[0]+state.x.ptr.p_double[1]-11, _state)+ae_sqr(state.x.ptr.p_double[0]+state.x.ptr.p_double[1]*state.x.ptr.p_double[1]-7, _state);
            othererrors = othererrors||ae_fp_greater(ae_fabs(v-state.f, _state)/ae_maxreal(v, 1, _state),100*ae_machineepsilon);
            
            /*
             * update info and continue
             */
            ae_v_move(&xlast.ptr.p_double[0], 1, &state.x.ptr.p_double[0], 1, ae_v_len(0,n-1));
            flast = state.f;
            itcnt = itcnt+1;
            continue;
        }
        if( state.needf )
        {
            nfunc = nfunc+1;
        }
        if( state.needfij )
        {
            nfunc = nfunc+1;
            njac = njac+1;
        }
        testnlequnit_testfunchbm(&state, _state);
    }
    nleqresults(&state, &x, &rep, _state);
    if( rep.terminationtype>0 )
    {
        othererrors = (othererrors||ae_fp_neq(xlast.ptr.p_double[0],x.ptr.p_double[0]))||ae_fp_neq(xlast.ptr.p_double[1],x.ptr.p_double[1]);
        v = ae_sqr(x.ptr.p_double[0]*x.ptr.p_double[0]+x.ptr.p_double[1]-11, _state)+ae_sqr(x.ptr.p_double[0]+x.ptr.p_double[1]*x.ptr.p_double[1]-7, _state);
        othererrors = othererrors||ae_fp_greater(ae_fabs(flast-v, _state)/ae_maxreal(v, 1, _state),100*ae_machineepsilon);
    }
    else
    {
        converror = ae_true;
    }
    othererrors = othererrors||rep.nfunc!=nfunc;
    othererrors = othererrors||rep.njac!=njac;
    othererrors = othererrors||rep.iterationscount!=itcnt-1;
    
    /*
     * Test ability to set limit on algorithm steps
     */
    ae_vector_set_length(&x, 2, _state);
    ae_vector_set_length(&xlast, 2, _state);
    x.ptr.p_double[0] = 20*ae_randomreal(_state)+20;
    x.ptr.p_double[1] = 20*ae_randomreal(_state)+20;
    xlast.ptr.p_double[0] = x.ptr.p_double[0];
    xlast.ptr.p_double[1] = x.ptr.p_double[1];
    stpmax = 0.1+0.1*ae_randomreal(_state);
    epsf = 1.0E-9;
    nleqcreatelm(2, 3, &x, &state, _state);
    nleqsetstpmax(&state, stpmax, _state);
    nleqsetcond(&state, epsf, 0, _state);
    nleqsetxrep(&state, ae_true, _state);
    while(nleqiteration(&state, _state))
    {
        if( state.needf||state.needfij )
        {
            testnlequnit_testfunchbm(&state, _state);
        }
        if( (state.needf||state.needfij)||state.xupdated )
        {
            othererrors = othererrors||ae_fp_greater(ae_sqrt(ae_sqr(state.x.ptr.p_double[0]-xlast.ptr.p_double[0], _state)+ae_sqr(state.x.ptr.p_double[1]-xlast.ptr.p_double[1], _state), _state),1.00001*stpmax);
        }
        if( state.xupdated )
        {
            xlast.ptr.p_double[0] = state.x.ptr.p_double[0];
            xlast.ptr.p_double[1] = state.x.ptr.p_double[1];
        }
    }
    
    /*
     * end
     */
    waserrors = (basicserrors||converror)||othererrors;
    if( !silent )
    {
        printf("TESTING NLEQ SOLVER\n");
        printf("BASIC FUNCTIONALITY:                      ");
        if( basicserrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("CONVERGENCE:                              ");
        if( converror )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        printf("OTHER PROPERTIES:                         ");
        if( othererrors )
        {
            printf("FAILED\n");
        }
        else
        {
            printf("OK\n");
        }
        if( waserrors )
        {
            printf("TEST FAILED\n");
        }
        else
        {
            printf("TEST PASSED\n");
        }
        printf("\n\n");
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Himmelblau's function

    F = (x^2+y-11)^2 + (x+y^2-7)^2

posed as system of M functions:

    f0 = x^2+y-11
    f1 = x+y^2-7

*************************************************************************/
static void testnlequnit_testfunchbm(nleqstate* state, ae_state *_state)
{
    double x;
    double y;


    ae_assert(state->needf||state->needfij, "TestNLEQUnit: internal error!", _state);
    x = state->x.ptr.p_double[0];
    y = state->x.ptr.p_double[1];
    if( state->needf )
    {
        state->f = ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state);
        return;
    }
    if( state->needfij )
    {
        state->fi.ptr.p_double[0] = x*x+y-11;
        state->fi.ptr.p_double[1] = x+y*y-7;
        state->j.ptr.pp_double[0][0] = 2*x;
        state->j.ptr.pp_double[0][1] = 1;
        state->j.ptr.pp_double[1][0] = 1;
        state->j.ptr.pp_double[1][1] = 2*y;
        return;
    }
}


/*************************************************************************
Himmelblau's function

    F = (x^2+y-11)^2 + (x+y^2-7)^2

posed as system of 1 function
*************************************************************************/
static void testnlequnit_testfunchb1(nleqstate* state, ae_state *_state)
{
    double x;
    double y;


    ae_assert(state->needf||state->needfij, "TestNLEQUnit: internal error!", _state);
    x = state->x.ptr.p_double[0];
    y = state->x.ptr.p_double[1];
    if( state->needf )
    {
        state->f = ae_sqr(ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state), _state);
        return;
    }
    if( state->needfij )
    {
        state->fi.ptr.p_double[0] = ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state);
        state->j.ptr.pp_double[0][0] = 2*(x*x+y-11)*2*x+2*(x+y*y-7);
        state->j.ptr.pp_double[0][1] = 2*(x*x+y-11)+2*(x+y*y-7)*2*y;
        return;
    }
}


/*************************************************************************
Shifted Himmelblau's function

    F = (x^2+y-11)^2 + (x+y^2-7)^2 + 1

posed as system of M functions:

    f0 = x^2+y-11
    f1 = x+y^2-7
    f2 = 1

This function is used to test algorithm on problem which has no solution.
*************************************************************************/
static void testnlequnit_testfuncshbm(nleqstate* state, ae_state *_state)
{
    double x;
    double y;


    ae_assert(state->needf||state->needfij, "TestNLEQUnit: internal error!", _state);
    x = state->x.ptr.p_double[0];
    y = state->x.ptr.p_double[1];
    if( state->needf )
    {
        state->f = ae_sqr(x*x+y-11, _state)+ae_sqr(x+y*y-7, _state)+1;
        return;
    }
    if( state->needfij )
    {
        state->fi.ptr.p_double[0] = x*x+y-11;
        state->fi.ptr.p_double[1] = x+y*y-7;
        state->fi.ptr.p_double[2] = 1;
        state->j.ptr.pp_double[0][0] = 2*x;
        state->j.ptr.pp_double[0][1] = 1;
        state->j.ptr.pp_double[1][0] = 1;
        state->j.ptr.pp_double[1][1] = 2*y;
        state->j.ptr.pp_double[2][0] = 0;
        state->j.ptr.pp_double[2][1] = 0;
        return;
    }
}








ae_bool testchebyshev(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double ferr;
    double threshold;
    double x;
    double v;
    ae_int_t pass;
    ae_int_t i;
    ae_int_t j;
    ae_int_t k;
    ae_int_t n;
    ae_int_t maxn;
    ae_vector c;
    ae_vector p1;
    ae_vector p2;
    ae_matrix a;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p1, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&p2, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);

    err = 0;
    sumerr = 0;
    cerr = 0;
    ferr = 0;
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Chebyshev polynomials of the first kind
     */
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 0, 0.00, _state)-1, _state), _state);
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 0, 0.33, _state)-1, _state), _state);
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 0, -0.42, _state)-1, _state), _state);
    x = 0.2;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.2, _state), _state);
    x = 0.4;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.4, _state), _state);
    x = 0.6;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.6, _state), _state);
    x = 0.8;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-0.8, _state), _state);
    x = 1.0;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 1, x, _state)-1.0, _state), _state);
    x = 0.2;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)+0.92, _state), _state);
    x = 0.4;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)+0.68, _state), _state);
    x = 0.6;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)+0.28, _state), _state);
    x = 0.8;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)-0.28, _state), _state);
    x = 1.0;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, 2, x, _state)-1.00, _state), _state);
    n = 10;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, n, 0.2, _state)-0.4284556288, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, n, 0.2, _state)+0.7996160205, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(1, n, 0.2, _state)+0.7483020370, _state), _state);
    
    /*
     * Testing Chebyshev polynomials of the second kind
     */
    n = 0;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-1.0000000000, _state), _state);
    n = 1;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-0.4000000000, _state), _state);
    n = 2;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.8400000000, _state), _state);
    n = 3;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.7360000000, _state), _state);
    n = 4;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-0.5456000000, _state), _state);
    n = 10;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)-0.6128946176, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.6770370970, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(chebyshevcalculate(2, n, 0.2, _state)+0.8837094564, _state), _state);
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 20;
    ae_vector_set_length(&c, maxn+1, _state);
    for(k=1; k<=2; k++)
    {
        for(pass=1; pass<=10; pass++)
        {
            x = 2*ae_randomreal(_state)-1;
            v = 0;
            for(n=0; n<=maxn; n++)
            {
                c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
                v = v+chebyshevcalculate(k, n, x, _state)*c.ptr.p_double[n];
                sumerr = ae_maxreal(sumerr, ae_fabs(v-chebyshevsum(&c, k, n, x, _state), _state), _state);
            }
        }
    }
    
    /*
     * Testing coefficients
     */
    chebyshevcoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    chebyshevcoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-1, _state), _state);
    chebyshevcoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+1, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-2, _state), _state);
    chebyshevcoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+3, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-4, _state), _state);
    chebyshevcoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]+8, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-8, _state), _state);
    chebyshevcoefficients(9, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-9, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-432, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[7]+576, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[8]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[9]-256, _state), _state);
    
    /*
     * Testing FromChebyshev
     */
    maxn = 10;
    ae_matrix_set_length(&a, maxn+1, maxn+1, _state);
    for(i=0; i<=maxn; i++)
    {
        for(j=0; j<=maxn; j++)
        {
            a.ptr.pp_double[i][j] = 0;
        }
        chebyshevcoefficients(i, &c, _state);
        ae_v_move(&a.ptr.pp_double[i][0], 1, &c.ptr.p_double[0], 1, ae_v_len(0,i));
    }
    ae_vector_set_length(&c, maxn+1, _state);
    ae_vector_set_length(&p1, maxn+1, _state);
    for(n=0; n<=maxn; n++)
    {
        for(pass=1; pass<=10; pass++)
        {
            for(i=0; i<=n; i++)
            {
                p1.ptr.p_double[i] = 0;
            }
            for(i=0; i<=n; i++)
            {
                c.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                v = c.ptr.p_double[i];
                ae_v_addd(&p1.ptr.p_double[0], 1, &a.ptr.pp_double[i][0], 1, ae_v_len(0,i), v);
            }
            fromchebyshev(&c, n, &p2, _state);
            for(i=0; i<=n; i++)
            {
                ferr = ae_maxreal(ferr, ae_fabs(p1.ptr.p_double[i]-p2.ptr.p_double[i], _state), _state);
            }
        }
    }
    
    /*
     * Reporting
     */
    waserrors = ((ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold))||ae_fp_greater(ferr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS\n");
        printf("Max error against table                   %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("FrobChebyshev error                       %5.2e\n",
            (double)(ferr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testhermite(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double threshold;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t pass;
    ae_vector c;
    double x;
    double v;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);

    err = 0;
    sumerr = 0;
    cerr = 0;
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Hermite polynomials
     */
    n = 0;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)-1, _state), _state);
    n = 1;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)-2, _state), _state);
    n = 2;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)-2, _state), _state);
    n = 3;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)+4, _state), _state);
    n = 4;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)+20, _state), _state);
    n = 5;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)+8, _state), _state);
    n = 6;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)-184, _state), _state);
    n = 7;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)-464, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)-230848, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(hermitecalculate(n, 1, _state)-280768, _state), _state);
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 10;
    ae_vector_set_length(&c, maxn+1, _state);
    for(pass=1; pass<=10; pass++)
    {
        x = 2*ae_randomreal(_state)-1;
        v = 0;
        for(n=0; n<=maxn; n++)
        {
            c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
            v = v+hermitecalculate(n, x, _state)*c.ptr.p_double[n];
            sumerr = ae_maxreal(sumerr, ae_fabs(v-hermitesum(&c, n, x, _state), _state), _state);
        }
    }
    
    /*
     * Testing coefficients
     */
    hermitecoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    hermitecoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-2, _state), _state);
    hermitecoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-4, _state), _state);
    hermitecoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+12, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-8, _state), _state);
    hermitecoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-12, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]+48, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-16, _state), _state);
    hermitecoefficients(5, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+160, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-32, _state), _state);
    hermitecoefficients(6, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]+480, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-64, _state), _state);
    
    /*
     * Reporting
     */
    waserrors = (ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE HERMITE POLYNOMIALS\n");
        printf("Max error                                 %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testlaguerre(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double threshold;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t pass;
    ae_vector c;
    double x;
    double v;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);

    err = 0;
    sumerr = 0;
    cerr = 0;
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Laguerre polynomials
     */
    n = 0;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)-1.0000000000, _state), _state);
    n = 1;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)-0.5000000000, _state), _state);
    n = 2;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)-0.1250000000, _state), _state);
    n = 3;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.1458333333, _state), _state);
    n = 4;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.3307291667, _state), _state);
    n = 5;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.4455729167, _state), _state);
    n = 6;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.5041449653, _state), _state);
    n = 7;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.5183392237, _state), _state);
    n = 8;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.4983629984, _state), _state);
    n = 9;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.4529195204, _state), _state);
    n = 10;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.3893744141, _state), _state);
    n = 11;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.3139072988, _state), _state);
    n = 12;
    err = ae_maxreal(err, ae_fabs(laguerrecalculate(n, 0.5, _state)+0.2316496389, _state), _state);
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 20;
    ae_vector_set_length(&c, maxn+1, _state);
    for(pass=1; pass<=10; pass++)
    {
        x = 2*ae_randomreal(_state)-1;
        v = 0;
        for(n=0; n<=maxn; n++)
        {
            c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
            v = v+laguerrecalculate(n, x, _state)*c.ptr.p_double[n];
            sumerr = ae_maxreal(sumerr, ae_fabs(v-laguerresum(&c, n, x, _state), _state), _state);
        }
    }
    
    /*
     * Testing coefficients
     */
    laguerrecoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    laguerrecoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+1, _state), _state);
    laguerrecoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)2/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)4/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)1/(double)2, _state), _state);
    laguerrecoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)6/(double)6, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)18/(double)6, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)9/(double)6, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)1/(double)6, _state), _state);
    laguerrecoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)24/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)96/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)72/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)16/(double)24, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)1/(double)24, _state), _state);
    laguerrecoefficients(5, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)120/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)600/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)600/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)200/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)25/(double)120, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]+(double)1/(double)120, _state), _state);
    laguerrecoefficients(6, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)720/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)4320/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)5400/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)2400/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)450/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]+(double)36/(double)720, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-(double)1/(double)720, _state), _state);
    
    /*
     * Reporting
     */
    waserrors = (ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS\n");
        printf("Max error                                 %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}








ae_bool testlegendre(ae_bool silent, ae_state *_state)
{
    ae_frame _frame_block;
    double err;
    double sumerr;
    double cerr;
    double threshold;
    ae_int_t n;
    ae_int_t maxn;
    ae_int_t i;
    ae_int_t pass;
    ae_vector c;
    double x;
    double v;
    double t;
    ae_bool waserrors;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&c, 0, DT_REAL, _state, ae_true);

    err = 0;
    sumerr = 0;
    cerr = 0;
    threshold = 1.0E-9;
    waserrors = ae_false;
    
    /*
     * Testing Legendre polynomials values
     */
    for(n=0; n<=10; n++)
    {
        legendrecoefficients(n, &c, _state);
        for(pass=1; pass<=10; pass++)
        {
            x = 2*ae_randomreal(_state)-1;
            v = legendrecalculate(n, x, _state);
            t = 1;
            for(i=0; i<=n; i++)
            {
                v = v-c.ptr.p_double[i]*t;
                t = t*x;
            }
            err = ae_maxreal(err, ae_fabs(v, _state), _state);
        }
    }
    
    /*
     * Testing Clenshaw summation
     */
    maxn = 20;
    ae_vector_set_length(&c, maxn+1, _state);
    for(pass=1; pass<=10; pass++)
    {
        x = 2*ae_randomreal(_state)-1;
        v = 0;
        for(n=0; n<=maxn; n++)
        {
            c.ptr.p_double[n] = 2*ae_randomreal(_state)-1;
            v = v+legendrecalculate(n, x, _state)*c.ptr.p_double[n];
            sumerr = ae_maxreal(sumerr, ae_fabs(v-legendresum(&c, n, x, _state), _state), _state);
        }
    }
    
    /*
     * Testing coefficients
     */
    legendrecoefficients(0, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-1, _state), _state);
    legendrecoefficients(1, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-1, _state), _state);
    legendrecoefficients(2, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]+(double)1/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-(double)3/(double)2, _state), _state);
    legendrecoefficients(3, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]+(double)3/(double)2, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-(double)5/(double)2, _state), _state);
    legendrecoefficients(4, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-(double)3/(double)8, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]+(double)30/(double)8, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-(double)35/(double)8, _state), _state);
    legendrecoefficients(9, &c, _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[0]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[1]-(double)315/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[2]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[3]+(double)4620/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[4]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[5]-(double)18018/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[6]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[7]+(double)25740/(double)128, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[8]-0, _state), _state);
    cerr = ae_maxreal(cerr, ae_fabs(c.ptr.p_double[9]-(double)12155/(double)128, _state), _state);
    
    /*
     * Reporting
     */
    waserrors = (ae_fp_greater(err,threshold)||ae_fp_greater(sumerr,threshold))||ae_fp_greater(cerr,threshold);
    if( !silent )
    {
        printf("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS\n");
        printf("Max error                                 %5.2e\n",
            (double)(err));
        printf("Summation error                           %5.2e\n",
            (double)(sumerr));
        printf("Coefficients error                        %5.2e\n",
            (double)(cerr));
        printf("Threshold                                 %5.2e\n",
            (double)(threshold));
        if( !waserrors )
        {
            printf("TEST PASSED\n");
        }
        else
        {
            printf("TEST FAILED\n");
        }
    }
    result = !waserrors;
    ae_frame_leave(_state);
    return result;
}



static ae_bool testalglibbasicsunit_testcomplexarithmetics(ae_bool silent,
     ae_state *_state);
static ae_bool testalglibbasicsunit_testieeespecial(ae_bool silent,
     ae_state *_state);
static ae_bool testalglibbasicsunit_testswapfunctions(ae_bool silent,
     ae_state *_state);
static ae_bool testalglibbasicsunit_testserializationfunctions(ae_bool silent,
     ae_state *_state);





void rec4serializationalloc(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * boolean fields
     */
    ae_serializer_alloc_entry(s);
    for(i=0; i<=v->b.cnt-1; i++)
    {
        ae_serializer_alloc_entry(s);
    }
    
    /*
     * integer fields
     */
    ae_serializer_alloc_entry(s);
    for(i=0; i<=v->i.cnt-1; i++)
    {
        ae_serializer_alloc_entry(s);
    }
    
    /*
     * real fields
     */
    ae_serializer_alloc_entry(s);
    for(i=0; i<=v->r.cnt-1; i++)
    {
        ae_serializer_alloc_entry(s);
    }
}


void rec4serializationserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state)
{
    ae_int_t i;


    
    /*
     * boolean fields
     */
    ae_serializer_serialize_int(s, v->b.cnt, _state);
    for(i=0; i<=v->b.cnt-1; i++)
    {
        ae_serializer_serialize_bool(s, v->b.ptr.p_bool[i], _state);
    }
    
    /*
     * integer fields
     */
    ae_serializer_serialize_int(s, v->i.cnt, _state);
    for(i=0; i<=v->i.cnt-1; i++)
    {
        ae_serializer_serialize_int(s, v->i.ptr.p_int[i], _state);
    }
    
    /*
     * real fields
     */
    ae_serializer_serialize_int(s, v->r.cnt, _state);
    for(i=0; i<=v->r.cnt-1; i++)
    {
        ae_serializer_serialize_double(s, v->r.ptr.p_double[i], _state);
    }
}


void rec4serializationunserialize(ae_serializer* s,
     rec4serialization* v,
     ae_state *_state)
{
    ae_int_t i;
    ae_int_t k;
    ae_bool bv;
    ae_int_t iv;
    double rv;

    _rec4serialization_clear(v);

    
    /*
     * boolean fields
     */
    ae_serializer_unserialize_int(s, &k, _state);
    if( k>0 )
    {
        ae_vector_set_length(&v->b, k, _state);
        for(i=0; i<=k-1; i++)
        {
            ae_serializer_unserialize_bool(s, &bv, _state);
            v->b.ptr.p_bool[i] = bv;
        }
    }
    
    /*
     * integer fields
     */
    ae_serializer_unserialize_int(s, &k, _state);
    if( k>0 )
    {
        ae_vector_set_length(&v->i, k, _state);
        for(i=0; i<=k-1; i++)
        {
            ae_serializer_unserialize_int(s, &iv, _state);
            v->i.ptr.p_int[i] = iv;
        }
    }
    
    /*
     * real fields
     */
    ae_serializer_unserialize_int(s, &k, _state);
    if( k>0 )
    {
        ae_vector_set_length(&v->r, k, _state);
        for(i=0; i<=k-1; i++)
        {
            ae_serializer_unserialize_double(s, &rv, _state);
            v->r.ptr.p_double[i] = rv;
        }
    }
}


ae_bool testalglibbasics(ae_bool silent, ae_state *_state)
{
    ae_bool result;


    result = ae_true;
    result = result&&testalglibbasicsunit_testcomplexarithmetics(silent, _state);
    result = result&&testalglibbasicsunit_testieeespecial(silent, _state);
    result = result&&testalglibbasicsunit_testswapfunctions(silent, _state);
    result = result&&testalglibbasicsunit_testserializationfunctions(silent, _state);
    if( !silent )
    {
        printf("\n\n");
    }
    return result;
}


/*************************************************************************
Complex arithmetics test
*************************************************************************/
static ae_bool testalglibbasicsunit_testcomplexarithmetics(ae_bool silent,
     ae_state *_state)
{
    ae_bool absc;
    ae_bool addcc;
    ae_bool addcr;
    ae_bool addrc;
    ae_bool subcc;
    ae_bool subcr;
    ae_bool subrc;
    ae_bool mulcc;
    ae_bool mulcr;
    ae_bool mulrc;
    ae_bool divcc;
    ae_bool divcr;
    ae_bool divrc;
    ae_complex ca;
    ae_complex cb;
    ae_complex res;
    double ra;
    double rb;
    double threshold;
    ae_int_t pass;
    ae_int_t passcount;
    ae_bool result;


    threshold = 100*ae_machineepsilon;
    passcount = 1000;
    result = ae_true;
    absc = ae_true;
    addcc = ae_true;
    addcr = ae_true;
    addrc = ae_true;
    subcc = ae_true;
    subcr = ae_true;
    subrc = ae_true;
    mulcc = ae_true;
    mulcr = ae_true;
    mulrc = ae_true;
    divcc = ae_true;
    divcr = ae_true;
    divrc = ae_true;
    for(pass=1; pass<=passcount; pass++)
    {
        
        /*
         * Test AbsC
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        ra = ae_c_abs(ca, _state);
        absc = absc&&ae_fp_less(ae_fabs(ra-ae_sqrt(ae_sqr(ca.x, _state)+ae_sqr(ca.y, _state), _state), _state),threshold);
        
        /*
         * test Add
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        cb.x = 2*ae_randomreal(_state)-1;
        cb.y = 2*ae_randomreal(_state)-1;
        ra = 2*ae_randomreal(_state)-1;
        rb = 2*ae_randomreal(_state)-1;
        res = ae_c_add(ca,cb);
        addcc = (addcc&&ae_fp_less(ae_fabs(res.x-ca.x-cb.x, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y-cb.y, _state),threshold);
        res = ae_c_add_d(ca,rb);
        addcr = (addcr&&ae_fp_less(ae_fabs(res.x-ca.x-rb, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y, _state),threshold);
        res = ae_c_add_d(cb,ra);
        addrc = (addrc&&ae_fp_less(ae_fabs(res.x-ra-cb.x, _state),threshold))&&ae_fp_less(ae_fabs(res.y-cb.y, _state),threshold);
        
        /*
         * test Sub
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        cb.x = 2*ae_randomreal(_state)-1;
        cb.y = 2*ae_randomreal(_state)-1;
        ra = 2*ae_randomreal(_state)-1;
        rb = 2*ae_randomreal(_state)-1;
        res = ae_c_sub(ca,cb);
        subcc = (subcc&&ae_fp_less(ae_fabs(res.x-(ca.x-cb.x), _state),threshold))&&ae_fp_less(ae_fabs(res.y-(ca.y-cb.y), _state),threshold);
        res = ae_c_sub_d(ca,rb);
        subcr = (subcr&&ae_fp_less(ae_fabs(res.x-(ca.x-rb), _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y, _state),threshold);
        res = ae_c_d_sub(ra,cb);
        subrc = (subrc&&ae_fp_less(ae_fabs(res.x-(ra-cb.x), _state),threshold))&&ae_fp_less(ae_fabs(res.y+cb.y, _state),threshold);
        
        /*
         * test Mul
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        cb.x = 2*ae_randomreal(_state)-1;
        cb.y = 2*ae_randomreal(_state)-1;
        ra = 2*ae_randomreal(_state)-1;
        rb = 2*ae_randomreal(_state)-1;
        res = ae_c_mul(ca,cb);
        mulcc = (mulcc&&ae_fp_less(ae_fabs(res.x-(ca.x*cb.x-ca.y*cb.y), _state),threshold))&&ae_fp_less(ae_fabs(res.y-(ca.x*cb.y+ca.y*cb.x), _state),threshold);
        res = ae_c_mul_d(ca,rb);
        mulcr = (mulcr&&ae_fp_less(ae_fabs(res.x-ca.x*rb, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y*rb, _state),threshold);
        res = ae_c_mul_d(cb,ra);
        mulrc = (mulrc&&ae_fp_less(ae_fabs(res.x-ra*cb.x, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ra*cb.y, _state),threshold);
        
        /*
         * test Div
         */
        ca.x = 2*ae_randomreal(_state)-1;
        ca.y = 2*ae_randomreal(_state)-1;
        do
        {
            cb.x = 2*ae_randomreal(_state)-1;
            cb.y = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_c_abs(cb, _state),0.5));
        ra = 2*ae_randomreal(_state)-1;
        do
        {
            rb = 2*ae_randomreal(_state)-1;
        }
        while(ae_fp_less_eq(ae_fabs(rb, _state),0.5));
        res = ae_c_div(ca,cb);
        divcc = (divcc&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).x-ca.x, _state),threshold))&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).y-ca.y, _state),threshold);
        res = ae_c_div_d(ca,rb);
        divcr = (divcr&&ae_fp_less(ae_fabs(res.x-ca.x/rb, _state),threshold))&&ae_fp_less(ae_fabs(res.y-ca.y/rb, _state),threshold);
        res = ae_c_d_div(ra,cb);
        divrc = (divrc&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).x-ra, _state),threshold))&&ae_fp_less(ae_fabs(ae_c_mul(res,cb).y, _state),threshold);
    }
    
    /*
     * summary
     */
    result = result&&absc;
    result = result&&addcc;
    result = result&&addcr;
    result = result&&addrc;
    result = result&&subcc;
    result = result&&subcr;
    result = result&&subrc;
    result = result&&mulcc;
    result = result&&mulcr;
    result = result&&mulrc;
    result = result&&divcc;
    result = result&&divcr;
    result = result&&divrc;
    if( !silent )
    {
        if( result )
        {
            printf("COMPLEX ARITHMETICS:                     OK\n");
        }
        else
        {
            printf("COMPLEX ARITHMETICS:                     FAILED\n");
            printf("* AddCC - - - - - - - - - - - - - - - -  ");
            if( addcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* AddCR - - - - - - - - - - - - - - - -  ");
            if( addcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* AddRC - - - - - - - - - - - - - - - -  ");
            if( addrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* SubCC - - - - - - - - - - - - - - - -  ");
            if( subcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* SubCR - - - - - - - - - - - - - - - -  ");
            if( subcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* SubRC - - - - - - - - - - - - - - - -  ");
            if( subrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* MulCC - - - - - - - - - - - - - - - -  ");
            if( mulcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* MulCR - - - - - - - - - - - - - - - -  ");
            if( mulcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* MulRC - - - - - - - - - - - - - - - -  ");
            if( mulrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* DivCC - - - - - - - - - - - - - - - -  ");
            if( divcc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* DivCR - - - - - - - - - - - - - - - -  ");
            if( divcr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* DivRC - - - - - - - - - - - - - - - -  ");
            if( divrc )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
        }
    }
    return result;
}


/*************************************************************************
Tests for IEEE special quantities
*************************************************************************/
static ae_bool testalglibbasicsunit_testieeespecial(ae_bool silent,
     ae_state *_state)
{
    ae_bool oknan;
    ae_bool okinf;
    ae_bool okother;
    double v1;
    double v2;
    ae_bool result;


    result = ae_true;
    oknan = ae_true;
    okinf = ae_true;
    okother = ae_true;
    
    /*
     * Test classification functions
     */
    okother = okother&&!ae_isinf(_state->v_nan, _state);
    okother = okother&&ae_isinf(_state->v_posinf, _state);
    okother = okother&&!ae_isinf(ae_maxrealnumber, _state);
    okother = okother&&!ae_isinf(1.0, _state);
    okother = okother&&!ae_isinf(ae_minrealnumber, _state);
    okother = okother&&!ae_isinf(0.0, _state);
    okother = okother&&!ae_isinf(-ae_minrealnumber, _state);
    okother = okother&&!ae_isinf(-1.0, _state);
    okother = okother&&!ae_isinf(-ae_maxrealnumber, _state);
    okother = okother&&ae_isinf(_state->v_neginf, _state);
    okother = okother&&!ae_isposinf(_state->v_nan, _state);
    okother = okother&&ae_isposinf(_state->v_posinf, _state);
    okother = okother&&!ae_isposinf(ae_maxrealnumber, _state);
    okother = okother&&!ae_isposinf(1.0, _state);
    okother = okother&&!ae_isposinf(ae_minrealnumber, _state);
    okother = okother&&!ae_isposinf(0.0, _state);
    okother = okother&&!ae_isposinf(-ae_minrealnumber, _state);
    okother = okother&&!ae_isposinf(-1.0, _state);
    okother = okother&&!ae_isposinf(-ae_maxrealnumber, _state);
    okother = okother&&!ae_isposinf(_state->v_neginf, _state);
    okother = okother&&!ae_isneginf(_state->v_nan, _state);
    okother = okother&&!ae_isneginf(_state->v_posinf, _state);
    okother = okother&&!ae_isneginf(ae_maxrealnumber, _state);
    okother = okother&&!ae_isneginf(1.0, _state);
    okother = okother&&!ae_isneginf(ae_minrealnumber, _state);
    okother = okother&&!ae_isneginf(0.0, _state);
    okother = okother&&!ae_isneginf(-ae_minrealnumber, _state);
    okother = okother&&!ae_isneginf(-1.0, _state);
    okother = okother&&!ae_isneginf(-ae_maxrealnumber, _state);
    okother = okother&&ae_isneginf(_state->v_neginf, _state);
    okother = okother&&ae_isnan(_state->v_nan, _state);
    okother = okother&&!ae_isnan(_state->v_posinf, _state);
    okother = okother&&!ae_isnan(ae_maxrealnumber, _state);
    okother = okother&&!ae_isnan(1.0, _state);
    okother = okother&&!ae_isnan(ae_minrealnumber, _state);
    okother = okother&&!ae_isnan(0.0, _state);
    okother = okother&&!ae_isnan(-ae_minrealnumber, _state);
    okother = okother&&!ae_isnan(-1.0, _state);
    okother = okother&&!ae_isnan(-ae_maxrealnumber, _state);
    okother = okother&&!ae_isnan(_state->v_neginf, _state);
    okother = okother&&!ae_isfinite(_state->v_nan, _state);
    okother = okother&&!ae_isfinite(_state->v_posinf, _state);
    okother = okother&&ae_isfinite(ae_maxrealnumber, _state);
    okother = okother&&ae_isfinite(1.0, _state);
    okother = okother&&ae_isfinite(ae_minrealnumber, _state);
    okother = okother&&ae_isfinite(0.0, _state);
    okother = okother&&ae_isfinite(-ae_minrealnumber, _state);
    okother = okother&&ae_isfinite(-1.0, _state);
    okother = okother&&ae_isfinite(-ae_maxrealnumber, _state);
    okother = okother&&!ae_isfinite(_state->v_neginf, _state);
    
    /*
     * Test NAN
     */
    v1 = _state->v_nan;
    v2 = _state->v_nan;
    oknan = oknan&&ae_isnan(v1, _state);
    oknan = oknan&&ae_fp_neq(v1,v2);
    oknan = oknan&&!ae_fp_eq(v1,v2);
    
    /*
     * Test INF:
     * * basic properties
     * * comparisons involving PosINF on one of the sides
     * * comparisons involving NegINF on one of the sides
     */
    v1 = _state->v_posinf;
    v2 = _state->v_neginf;
    okinf = okinf&&ae_isinf(_state->v_posinf, _state);
    okinf = okinf&&ae_isinf(v1, _state);
    okinf = okinf&&ae_isinf(_state->v_neginf, _state);
    okinf = okinf&&ae_isinf(v2, _state);
    okinf = okinf&&ae_isposinf(_state->v_posinf, _state);
    okinf = okinf&&ae_isposinf(v1, _state);
    okinf = okinf&&!ae_isposinf(_state->v_neginf, _state);
    okinf = okinf&&!ae_isposinf(v2, _state);
    okinf = okinf&&!ae_isneginf(_state->v_posinf, _state);
    okinf = okinf&&!ae_isneginf(v1, _state);
    okinf = okinf&&ae_isneginf(_state->v_neginf, _state);
    okinf = okinf&&ae_isneginf(v2, _state);
    okinf = okinf&&ae_fp_eq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&ae_fp_eq(_state->v_posinf,v1);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,v2);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,0);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,1.2);
    okinf = okinf&&!ae_fp_eq(_state->v_posinf,-1.2);
    okinf = okinf&&ae_fp_eq(v1,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(v2,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(0,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_neq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&!ae_fp_neq(_state->v_posinf,v1);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,v2);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,0);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,1.2);
    okinf = okinf&&ae_fp_neq(_state->v_posinf,-1.2);
    okinf = okinf&&!ae_fp_neq(v1,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(v2,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(0,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,v1);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,v2);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,0);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,1.2);
    okinf = okinf&&!ae_fp_less(_state->v_posinf,-1.2);
    okinf = okinf&&!ae_fp_less(v1,_state->v_posinf);
    okinf = okinf&&ae_fp_less(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less(v2,_state->v_posinf);
    okinf = okinf&&ae_fp_less(0,_state->v_posinf);
    okinf = okinf&&ae_fp_less(1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_less(-1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_posinf,v1);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,v2);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,0);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,1.2);
    okinf = okinf&&!ae_fp_less_eq(_state->v_posinf,-1.2);
    okinf = okinf&&ae_fp_less_eq(v1,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(v2,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(0,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_posinf,v1);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,v2);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,0);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,1.2);
    okinf = okinf&&ae_fp_greater(_state->v_posinf,-1.2);
    okinf = okinf&&!ae_fp_greater(v1,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(v2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(0,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(-1.2,_state->v_posinf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,_state->v_posinf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,v1);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,v2);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,0);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,1.2);
    okinf = okinf&&ae_fp_greater_eq(_state->v_posinf,-1.2);
    okinf = okinf&&ae_fp_greater_eq(v1,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(v2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(0,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(-1.2,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,v1);
    okinf = okinf&&ae_fp_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_eq(_state->v_neginf,v2);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,0);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,1.2);
    okinf = okinf&&!ae_fp_eq(_state->v_neginf,-1.2);
    okinf = okinf&&!ae_fp_eq(v1,_state->v_neginf);
    okinf = okinf&&ae_fp_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_eq(v2,_state->v_neginf);
    okinf = okinf&&!ae_fp_eq(0,_state->v_neginf);
    okinf = okinf&&!ae_fp_eq(1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_eq(-1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,v1);
    okinf = okinf&&!ae_fp_neq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_neq(_state->v_neginf,v2);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,0);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,1.2);
    okinf = okinf&&ae_fp_neq(_state->v_neginf,-1.2);
    okinf = okinf&&ae_fp_neq(v1,_state->v_neginf);
    okinf = okinf&&!ae_fp_neq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_neq(v2,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(0,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_neq(-1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_less(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less(_state->v_neginf,v1);
    okinf = okinf&&!ae_fp_less(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(_state->v_neginf,v2);
    okinf = okinf&&ae_fp_less(_state->v_neginf,0);
    okinf = okinf&&ae_fp_less(_state->v_neginf,1.2);
    okinf = okinf&&ae_fp_less(_state->v_neginf,-1.2);
    okinf = okinf&&!ae_fp_less(v1,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(v2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(0,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less(-1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,v1);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,v2);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,0);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,1.2);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,-1.2);
    okinf = okinf&&!ae_fp_less_eq(v1,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_less_eq(v2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq(0,_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq(1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_less_eq(-1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,v1);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,v2);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,0);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,1.2);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,-1.2);
    okinf = okinf&&ae_fp_greater(v1,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater(v2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater(0,_state->v_neginf);
    okinf = okinf&&ae_fp_greater(1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater(-1.2,_state->v_neginf);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,_state->v_posinf);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,v1);
    okinf = okinf&&ae_fp_greater_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_neginf,v2);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,0);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,1.2);
    okinf = okinf&&!ae_fp_greater_eq(_state->v_neginf,-1.2);
    okinf = okinf&&ae_fp_greater_eq(v1,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(_state->v_neginf,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(v2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(0,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(1.2,_state->v_neginf);
    okinf = okinf&&ae_fp_greater_eq(-1.2,_state->v_neginf);
    
    /*
     * summary
     */
    result = result&&oknan;
    result = result&&okinf;
    result = result&&okother;
    if( !silent )
    {
        if( result )
        {
            printf("IEEE SPECIAL VALUES:                     OK\n");
        }
        else
        {
            printf("IEEE SPECIAL VALUES:                     FAILED\n");
            printf("* NAN - - - - - - - - - - - - - - - - -  ");
            if( oknan )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* INF - - - - - - - - - - - - - - - - -  ");
            if( okinf )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* FUNCTIONS - - - - - - - - - - - - - -  ");
            if( okother )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
        }
    }
    return result;
}


/*************************************************************************
Tests for swapping functions
*************************************************************************/
static ae_bool testalglibbasicsunit_testswapfunctions(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool okb1;
    ae_bool okb2;
    ae_bool oki1;
    ae_bool oki2;
    ae_bool okr1;
    ae_bool okr2;
    ae_bool okc1;
    ae_bool okc2;
    ae_vector b11;
    ae_vector b12;
    ae_vector i11;
    ae_vector i12;
    ae_vector r11;
    ae_vector r12;
    ae_vector c11;
    ae_vector c12;
    ae_matrix b21;
    ae_matrix b22;
    ae_matrix i21;
    ae_matrix i22;
    ae_matrix r21;
    ae_matrix r22;
    ae_matrix c21;
    ae_matrix c22;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    ae_vector_init(&b11, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&b12, 0, DT_BOOL, _state, ae_true);
    ae_vector_init(&i11, 0, DT_INT, _state, ae_true);
    ae_vector_init(&i12, 0, DT_INT, _state, ae_true);
    ae_vector_init(&r11, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&r12, 0, DT_REAL, _state, ae_true);
    ae_vector_init(&c11, 0, DT_COMPLEX, _state, ae_true);
    ae_vector_init(&c12, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&b21, 0, 0, DT_BOOL, _state, ae_true);
    ae_matrix_init(&b22, 0, 0, DT_BOOL, _state, ae_true);
    ae_matrix_init(&i21, 0, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&i22, 0, 0, DT_INT, _state, ae_true);
    ae_matrix_init(&r21, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&r22, 0, 0, DT_REAL, _state, ae_true);
    ae_matrix_init(&c21, 0, 0, DT_COMPLEX, _state, ae_true);
    ae_matrix_init(&c22, 0, 0, DT_COMPLEX, _state, ae_true);

    result = ae_true;
    okb1 = ae_true;
    okb2 = ae_true;
    oki1 = ae_true;
    oki2 = ae_true;
    okr1 = ae_true;
    okr2 = ae_true;
    okc1 = ae_true;
    okc2 = ae_true;
    
    /*
     * Test B1 swaps
     */
    ae_vector_set_length(&b11, 1, _state);
    ae_vector_set_length(&b12, 2, _state);
    b11.ptr.p_bool[0] = ae_true;
    b12.ptr.p_bool[0] = ae_false;
    b12.ptr.p_bool[1] = ae_true;
    ae_swap_vectors(&b11, &b12);
    if( b11.cnt==2&&b12.cnt==1 )
    {
        okb1 = okb1&&!b11.ptr.p_bool[0];
        okb1 = okb1&&b11.ptr.p_bool[1];
        okb1 = okb1&&b12.ptr.p_bool[0];
    }
    else
    {
        okb1 = ae_false;
    }
    
    /*
     * Test I1 swaps
     */
    ae_vector_set_length(&i11, 1, _state);
    ae_vector_set_length(&i12, 2, _state);
    i11.ptr.p_int[0] = 1;
    i12.ptr.p_int[0] = 2;
    i12.ptr.p_int[1] = 3;
    ae_swap_vectors(&i11, &i12);
    if( i11.cnt==2&&i12.cnt==1 )
    {
        oki1 = oki1&&i11.ptr.p_int[0]==2;
        oki1 = oki1&&i11.ptr.p_int[1]==3;
        oki1 = oki1&&i12.ptr.p_int[0]==1;
    }
    else
    {
        oki1 = ae_false;
    }
    
    /*
     * Test R1 swaps
     */
    ae_vector_set_length(&r11, 1, _state);
    ae_vector_set_length(&r12, 2, _state);
    r11.ptr.p_double[0] = 1.5;
    r12.ptr.p_double[0] = 2.5;
    r12.ptr.p_double[1] = 3.5;
    ae_swap_vectors(&r11, &r12);
    if( r11.cnt==2&&r12.cnt==1 )
    {
        okr1 = okr1&&ae_fp_eq(r11.ptr.p_double[0],2.5);
        okr1 = okr1&&ae_fp_eq(r11.ptr.p_double[1],3.5);
        okr1 = okr1&&ae_fp_eq(r12.ptr.p_double[0],1.5);
    }
    else
    {
        okr1 = ae_false;
    }
    
    /*
     * Test C1 swaps
     */
    ae_vector_set_length(&c11, 1, _state);
    ae_vector_set_length(&c12, 2, _state);
    c11.ptr.p_complex[0] = ae_complex_from_d(1);
    c12.ptr.p_complex[0] = ae_complex_from_d(2);
    c12.ptr.p_complex[1] = ae_complex_from_d(3);
    ae_swap_vectors(&c11, &c12);
    if( c11.cnt==2&&c12.cnt==1 )
    {
        okc1 = okc1&&ae_c_eq_d(c11.ptr.p_complex[0],2);
        okc1 = okc1&&ae_c_eq_d(c11.ptr.p_complex[1],3);
        okc1 = okc1&&ae_c_eq_d(c12.ptr.p_complex[0],1);
    }
    else
    {
        okc1 = ae_false;
    }
    
    /*
     * Test B2 swaps
     */
    ae_matrix_set_length(&b21, 1, 2, _state);
    ae_matrix_set_length(&b22, 2, 1, _state);
    b21.ptr.pp_bool[0][0] = ae_true;
    b21.ptr.pp_bool[0][1] = ae_false;
    b22.ptr.pp_bool[0][0] = ae_false;
    b22.ptr.pp_bool[1][0] = ae_true;
    ae_swap_matrices(&b21, &b22);
    if( ((b21.rows==2&&b21.cols==1)&&b22.rows==1)&&b22.cols==2 )
    {
        okb2 = okb2&&!b21.ptr.pp_bool[0][0];
        okb2 = okb2&&b21.ptr.pp_bool[1][0];
        okb2 = okb2&&b22.ptr.pp_bool[0][0];
        okb2 = okb2&&!b22.ptr.pp_bool[0][1];
    }
    else
    {
        okb2 = ae_false;
    }
    
    /*
     * Test I2 swaps
     */
    ae_matrix_set_length(&i21, 1, 2, _state);
    ae_matrix_set_length(&i22, 2, 1, _state);
    i21.ptr.pp_int[0][0] = 1;
    i21.ptr.pp_int[0][1] = 2;
    i22.ptr.pp_int[0][0] = 3;
    i22.ptr.pp_int[1][0] = 4;
    ae_swap_matrices(&i21, &i22);
    if( ((i21.rows==2&&i21.cols==1)&&i22.rows==1)&&i22.cols==2 )
    {
        oki2 = oki2&&i21.ptr.pp_int[0][0]==3;
        oki2 = oki2&&i21.ptr.pp_int[1][0]==4;
        oki2 = oki2&&i22.ptr.pp_int[0][0]==1;
        oki2 = oki2&&i22.ptr.pp_int[0][1]==2;
    }
    else
    {
        oki2 = ae_false;
    }
    
    /*
     * Test R2 swaps
     */
    ae_matrix_set_length(&r21, 1, 2, _state);
    ae_matrix_set_length(&r22, 2, 1, _state);
    r21.ptr.pp_double[0][0] = 1;
    r21.ptr.pp_double[0][1] = 2;
    r22.ptr.pp_double[0][0] = 3;
    r22.ptr.pp_double[1][0] = 4;
    ae_swap_matrices(&r21, &r22);
    if( ((r21.rows==2&&r21.cols==1)&&r22.rows==1)&&r22.cols==2 )
    {
        okr2 = okr2&&ae_fp_eq(r21.ptr.pp_double[0][0],3);
        okr2 = okr2&&ae_fp_eq(r21.ptr.pp_double[1][0],4);
        okr2 = okr2&&ae_fp_eq(r22.ptr.pp_double[0][0],1);
        okr2 = okr2&&ae_fp_eq(r22.ptr.pp_double[0][1],2);
    }
    else
    {
        okr2 = ae_false;
    }
    
    /*
     * Test C2 swaps
     */
    ae_matrix_set_length(&c21, 1, 2, _state);
    ae_matrix_set_length(&c22, 2, 1, _state);
    c21.ptr.pp_complex[0][0] = ae_complex_from_d(1);
    c21.ptr.pp_complex[0][1] = ae_complex_from_d(2);
    c22.ptr.pp_complex[0][0] = ae_complex_from_d(3);
    c22.ptr.pp_complex[1][0] = ae_complex_from_d(4);
    ae_swap_matrices(&c21, &c22);
    if( ((c21.rows==2&&c21.cols==1)&&c22.rows==1)&&c22.cols==2 )
    {
        okc2 = okc2&&ae_c_eq_d(c21.ptr.pp_complex[0][0],3);
        okc2 = okc2&&ae_c_eq_d(c21.ptr.pp_complex[1][0],4);
        okc2 = okc2&&ae_c_eq_d(c22.ptr.pp_complex[0][0],1);
        okc2 = okc2&&ae_c_eq_d(c22.ptr.pp_complex[0][1],2);
    }
    else
    {
        okc2 = ae_false;
    }
    
    /*
     * summary
     */
    result = result&&okb1;
    result = result&&okb2;
    result = result&&oki1;
    result = result&&oki2;
    result = result&&okr1;
    result = result&&okr2;
    result = result&&okc1;
    result = result&&okc2;
    if( !silent )
    {
        if( result )
        {
            printf("SWAPPING FUNCTIONS:                      OK\n");
        }
        else
        {
            printf("SWAPPING FUNCTIONS:                      FAILED\n");
        }
    }
    ae_frame_leave(_state);
    return result;
}


/*************************************************************************
Tests for swapping functions
*************************************************************************/
static ae_bool testalglibbasicsunit_testserializationfunctions(ae_bool silent,
     ae_state *_state)
{
    ae_frame _frame_block;
    ae_bool okb;
    ae_bool oki;
    ae_bool okr;
    ae_int_t nb;
    ae_int_t ni;
    ae_int_t nr;
    ae_int_t i;
    rec4serialization r0;
    rec4serialization r1;
    ae_bool result;

    ae_frame_make(_state, &_frame_block);
    _rec4serialization_init(&r0, _state, ae_true);
    _rec4serialization_init(&r1, _state, ae_true);

    result = ae_true;
    okb = ae_true;
    oki = ae_true;
    okr = ae_true;
    for(nb=1; nb<=4; nb++)
    {
        for(ni=1; ni<=4; ni++)
        {
            for(nr=1; nr<=4; nr++)
            {
                ae_vector_set_length(&r0.b, nb, _state);
                for(i=0; i<=nb-1; i++)
                {
                    r0.b.ptr.p_bool[i] = ae_randominteger(2, _state)!=0;
                }
                ae_vector_set_length(&r0.i, ni, _state);
                for(i=0; i<=ni-1; i++)
                {
                    r0.i.ptr.p_int[i] = ae_randominteger(10, _state)-5;
                }
                ae_vector_set_length(&r0.r, nr, _state);
                for(i=0; i<=nr-1; i++)
                {
                    r0.r.ptr.p_double[i] = 2*ae_randomreal(_state)-1;
                }
                {
                    /*
                     * This code passes data structure through serializers
                     * (serializes it to string and loads back)
                     */
                    ae_serializer _local_serializer;
                    ae_int_t _local_ssize;
                    ae_frame _local_frame_block;
                    ae_dyn_block _local_dynamic_block;
                    
                    ae_frame_make(_state, &_local_frame_block);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_alloc_start(&_local_serializer);
                    rec4serializationalloc(&_local_serializer, &r0, _state);
                    _local_ssize = ae_serializer_get_alloc_size(&_local_serializer);
                    ae_db_malloc(&_local_dynamic_block, _local_ssize+1, _state, ae_true);
                    ae_serializer_sstart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    rec4serializationserialize(&_local_serializer, &r0, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_serializer_init(&_local_serializer);
                    ae_serializer_ustart_str(&_local_serializer, (char*)_local_dynamic_block.ptr);
                    rec4serializationunserialize(&_local_serializer, &r1, _state);
                    ae_serializer_stop(&_local_serializer);
                    ae_serializer_clear(&_local_serializer);
                    
                    ae_frame_leave(_state);
                }
                if( (r0.b.cnt==r1.b.cnt&&r0.i.cnt==r1.i.cnt)&&r0.r.cnt==r1.r.cnt )
                {
                    for(i=0; i<=nb-1; i++)
                    {
                        okb = okb&&((r0.b.ptr.p_bool[i]&&r1.b.ptr.p_bool[i])||(!r0.b.ptr.p_bool[i]&&!r1.b.ptr.p_bool[i]));
                    }
                    for(i=0; i<=ni-1; i++)
                    {
                        oki = oki&&r0.i.ptr.p_int[i]==r1.i.ptr.p_int[i];
                    }
                    for(i=0; i<=nr-1; i++)
                    {
                        okr = okr&&ae_fp_eq(r0.r.ptr.p_double[i],r1.r.ptr.p_double[i]);
                    }
                }
                else
                {
                    oki = ae_false;
                }
            }
        }
    }
    
    /*
     * summary
     */
    result = result&&okb;
    result = result&&oki;
    result = result&&okr;
    if( !silent )
    {
        if( result )
        {
            printf("SERIALIZATION FUNCTIONS:                 OK\n");
        }
        else
        {
            printf("SERIALIZATION FUNCTIONS:                 FAILED\n");
            printf("* BOOLEAN - - - - - - - - - - - - - - -  ");
            if( okb )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* INTEGER - - - - - - - - - - - - - - -  ");
            if( oki )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
            printf("* REAL  - - - - - - - - - - - - - - - -  ");
            if( okr )
            {
                printf("OK\n");
            }
            else
            {
                printf("FAILED\n");
            }
        }
    }
    ae_frame_leave(_state);
    return result;
}


ae_bool _rec1_init(rec1* p, ae_state *_state, ae_bool make_automatic)
{
    if( !ae_vector_init(&p->b1field, 0, DT_BOOL, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init(&p->r1field, 0, DT_REAL, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init(&p->i1field, 0, DT_INT, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init(&p->c1field, 0, DT_COMPLEX, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init(&p->b2field, 0, 0, DT_BOOL, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init(&p->r2field, 0, 0, DT_REAL, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init(&p->i2field, 0, 0, DT_INT, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init(&p->c2field, 0, 0, DT_COMPLEX, _state, make_automatic) )
        return ae_false;
    return ae_true;
}


ae_bool _rec1_init_copy(rec1* dst, rec1* src, ae_state *_state, ae_bool make_automatic)
{
    dst->bfield = src->bfield;
    dst->rfield = src->rfield;
    dst->ifield = src->ifield;
    dst->cfield = src->cfield;
    if( !ae_vector_init_copy(&dst->b1field, &src->b1field, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init_copy(&dst->r1field, &src->r1field, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init_copy(&dst->i1field, &src->i1field, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init_copy(&dst->c1field, &src->c1field, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init_copy(&dst->b2field, &src->b2field, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init_copy(&dst->r2field, &src->r2field, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init_copy(&dst->i2field, &src->i2field, _state, make_automatic) )
        return ae_false;
    if( !ae_matrix_init_copy(&dst->c2field, &src->c2field, _state, make_automatic) )
        return ae_false;
    return ae_true;
}


void _rec1_clear(rec1* p)
{
    ae_vector_clear(&p->b1field);
    ae_vector_clear(&p->r1field);
    ae_vector_clear(&p->i1field);
    ae_vector_clear(&p->c1field);
    ae_matrix_clear(&p->b2field);
    ae_matrix_clear(&p->r2field);
    ae_matrix_clear(&p->i2field);
    ae_matrix_clear(&p->c2field);
}


ae_bool _rec4serialization_init(rec4serialization* p, ae_state *_state, ae_bool make_automatic)
{
    if( !ae_vector_init(&p->b, 0, DT_BOOL, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init(&p->i, 0, DT_INT, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init(&p->r, 0, DT_REAL, _state, make_automatic) )
        return ae_false;
    return ae_true;
}


ae_bool _rec4serialization_init_copy(rec4serialization* dst, rec4serialization* src, ae_state *_state, ae_bool make_automatic)
{
    if( !ae_vector_init_copy(&dst->b, &src->b, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init_copy(&dst->i, &src->i, _state, make_automatic) )
        return ae_false;
    if( !ae_vector_init_copy(&dst->r, &src->r, _state, make_automatic) )
        return ae_false;
    return ae_true;
}


void _rec4serialization_clear(rec4serialization* p)
{
    ae_vector_clear(&p->b);
    ae_vector_clear(&p->i);
    ae_vector_clear(&p->r);
}



ae_bool call_unittest(unsigned seed, ae_bool(*testfunc)(ae_bool, ae_state*), int *psticky)
{
#ifndef AE_USE_CPP_ERROR_HANDLING
    ae_state _alglib_env_state;
    ae_frame _frame_block;
    jmp_buf _break_jump;
    ae_bool result;
    
    ae_state_init(&_alglib_env_state);
    if( setjmp(_break_jump) )
    {
        *psticky = 1;
        return ae_false;
    }
    ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
    ae_frame_make(&_alglib_env_state, &_frame_block);
    srand(seed);
    result = testfunc(ae_true, &_alglib_env_state);
    ae_state_clear(&_alglib_env_state);
    if( !result )
        *psticky = 1;
    return result;
#else
    try
    {
        ae_state _alglib_env_state;
        ae_frame _frame_block;
        ae_bool result;
    
        ae_state_init(&_alglib_env_state);
        ae_frame_make(&_alglib_env_state, &_frame_block);
        srand(seed);
        result = testfunc(ae_true, &_alglib_env_state);
        ae_state_clear(&_alglib_env_state);
        if( !result )
            *psticky = 1;
        return result;
    }
    catch(...)
    {
        *psticky = 1;
        return ae_false;
    }
#endif
}

int main(int argc, char **argv)
{
    unsigned seed;
    int result;
    double a;
    ae_int32_t *p;
    if( argc==2 )
        seed = (unsigned)atoi(argv[1]);
    else
    {
        time_t t;
        seed = (unsigned)time(&t);
    }
    
    /* 
     * Compiler
     */
#if AE_COMPILER==AE_GNUC
    printf("COMPILER: GCC\n");
#elif AE_COMPILER==AE_SUNC
    printf("COMPILER: SunStudio\n");
#elif AE_COMPILER==AE_MSVC
    printf("COMPILER: MSVC\n");
#else
    printf("COMPILER: unknown\n");
#endif
    
    /*
     * Architecture
     */
    if( sizeof(void*)==4 )
        printf("HARDWARE: 32-bit\n");
    else if( sizeof(void*)==8 )
        printf("HARDWARE: 64-bit\n");
    else
        printf("HARDWARE: strange (non-32, non-64)\n");
    
    /* 
     * determine endianness of hardware.
     * 1983 is a good number - non-periodic double representation allow us to
     * easily distinguish between upper and lower halfs and to detect mixed endian hardware.
     */
    a = 1.0/1983.0; 
    p = (ae_int32_t*)(&a);
    if( p[1]==0x3f408642 )
        printf("HARDWARE: little-endian\n");
    else if( p[0]==0x3f408642 )
        printf("HARDWARE: big-endian\n");
    else
        printf("HARDWARE: mixed-endian\n");
    
    /* 
     * CPU (as defined)
     */
#if AE_CPU==AE_INTEL
    printf("CPU: Intel\n");
#elif AE_CPU==AE_SPARC
    printf("CPU: SPARC\n");
#else
    printf("CPU: unknown\n");
#endif
    
    /* 
     * CPUID results
     */
    printf("CPUID: %s\n", ae_cpuid()&CPU_SSE2 ? "sse2" : "");

    /*
     * now we are ready to test!
     */
    result = 0;
    if( call_unittest(seed, testhqrnd, &result) )
        printf("%-32s OK\n", "hqrnd");
    else
        printf("%-32s FAILED(seed=%u)\n", "hqrnd", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testtsort, &result) )
        printf("%-32s OK\n", "tsort");
    else
        printf("%-32s FAILED(seed=%u)\n", "tsort", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testnearestneighbor, &result) )
        printf("%-32s OK\n", "nearestneighbor");
    else
        printf("%-32s FAILED(seed=%u)\n", "nearestneighbor", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testablas, &result) )
        printf("%-32s OK\n", "ablas");
    else
        printf("%-32s FAILED(seed=%u)\n", "ablas", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testbasestat, &result) )
        printf("%-32s OK\n", "basestat");
    else
        printf("%-32s FAILED(seed=%u)\n", "basestat", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testbdss, &result) )
        printf("%-32s OK\n", "bdss");
    else
        printf("%-32s FAILED(seed=%u)\n", "bdss", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testdforest, &result) )
        printf("%-32s OK\n", "dforest");
    else
        printf("%-32s FAILED(seed=%u)\n", "dforest", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testblas, &result) )
        printf("%-32s OK\n", "blas");
    else
        printf("%-32s FAILED(seed=%u)\n", "blas", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testkmeans, &result) )
        printf("%-32s OK\n", "kmeans");
    else
        printf("%-32s FAILED(seed=%u)\n", "kmeans", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testhblas, &result) )
        printf("%-32s OK\n", "hblas");
    else
        printf("%-32s FAILED(seed=%u)\n", "hblas", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testreflections, &result) )
        printf("%-32s OK\n", "reflections");
    else
        printf("%-32s FAILED(seed=%u)\n", "reflections", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testcreflections, &result) )
        printf("%-32s OK\n", "creflections");
    else
        printf("%-32s FAILED(seed=%u)\n", "creflections", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testsblas, &result) )
        printf("%-32s OK\n", "sblas");
    else
        printf("%-32s FAILED(seed=%u)\n", "sblas", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testortfac, &result) )
        printf("%-32s OK\n", "ortfac");
    else
        printf("%-32s FAILED(seed=%u)\n", "ortfac", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testevd, &result) )
        printf("%-32s OK\n", "evd");
    else
        printf("%-32s FAILED(seed=%u)\n", "evd", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testmatgen, &result) )
        printf("%-32s OK\n", "matgen");
    else
        printf("%-32s FAILED(seed=%u)\n", "matgen", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testtrfac, &result) )
        printf("%-32s OK\n", "trfac");
    else
        printf("%-32s FAILED(seed=%u)\n", "trfac", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testtrlinsolve, &result) )
        printf("%-32s OK\n", "trlinsolve");
    else
        printf("%-32s FAILED(seed=%u)\n", "trlinsolve", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testsafesolve, &result) )
        printf("%-32s OK\n", "safesolve");
    else
        printf("%-32s FAILED(seed=%u)\n", "safesolve", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testrcond, &result) )
        printf("%-32s OK\n", "rcond");
    else
        printf("%-32s FAILED(seed=%u)\n", "rcond", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testmatinv, &result) )
        printf("%-32s OK\n", "matinv");
    else
        printf("%-32s FAILED(seed=%u)\n", "matinv", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testlda, &result) )
        printf("%-32s OK\n", "lda");
    else
        printf("%-32s FAILED(seed=%u)\n", "lda", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testgammafunc, &result) )
        printf("%-32s OK\n", "gammafunc");
    else
        printf("%-32s FAILED(seed=%u)\n", "gammafunc", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testbdsvd, &result) )
        printf("%-32s OK\n", "bdsvd");
    else
        printf("%-32s FAILED(seed=%u)\n", "bdsvd", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testsvd, &result) )
        printf("%-32s OK\n", "svd");
    else
        printf("%-32s FAILED(seed=%u)\n", "svd", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testlinreg, &result) )
        printf("%-32s OK\n", "linreg");
    else
        printf("%-32s FAILED(seed=%u)\n", "linreg", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testxblas, &result) )
        printf("%-32s OK\n", "xblas");
    else
        printf("%-32s FAILED(seed=%u)\n", "xblas", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testdensesolver, &result) )
        printf("%-32s OK\n", "densesolver");
    else
        printf("%-32s FAILED(seed=%u)\n", "densesolver", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testlinmin, &result) )
        printf("%-32s OK\n", "linmin");
    else
        printf("%-32s FAILED(seed=%u)\n", "linmin", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testmincg, &result) )
        printf("%-32s OK\n", "mincg");
    else
        printf("%-32s FAILED(seed=%u)\n", "mincg", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testminbleic, &result) )
        printf("%-32s OK\n", "minbleic");
    else
        printf("%-32s FAILED(seed=%u)\n", "minbleic", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testmcpd, &result) )
        printf("%-32s OK\n", "mcpd");
    else
        printf("%-32s FAILED(seed=%u)\n", "mcpd", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testfbls, &result) )
        printf("%-32s OK\n", "fbls");
    else
        printf("%-32s FAILED(seed=%u)\n", "fbls", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testminlbfgs, &result) )
        printf("%-32s OK\n", "minlbfgs");
    else
        printf("%-32s FAILED(seed=%u)\n", "minlbfgs", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testmlptrain, &result) )
        printf("%-32s OK\n", "mlptrain");
    else
        printf("%-32s FAILED(seed=%u)\n", "mlptrain", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testmlpe, &result) )
        printf("%-32s OK\n", "mlpe");
    else
        printf("%-32s FAILED(seed=%u)\n", "mlpe", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testpca, &result) )
        printf("%-32s OK\n", "pca");
    else
        printf("%-32s FAILED(seed=%u)\n", "pca", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testodesolver, &result) )
        printf("%-32s OK\n", "odesolver");
    else
        printf("%-32s FAILED(seed=%u)\n", "odesolver", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testfft, &result) )
        printf("%-32s OK\n", "fft");
    else
        printf("%-32s FAILED(seed=%u)\n", "fft", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testconv, &result) )
        printf("%-32s OK\n", "conv");
    else
        printf("%-32s FAILED(seed=%u)\n", "conv", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testcorr, &result) )
        printf("%-32s OK\n", "corr");
    else
        printf("%-32s FAILED(seed=%u)\n", "corr", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testfht, &result) )
        printf("%-32s OK\n", "fht");
    else
        printf("%-32s FAILED(seed=%u)\n", "fht", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testgq, &result) )
        printf("%-32s OK\n", "gq");
    else
        printf("%-32s FAILED(seed=%u)\n", "gq", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testgkq, &result) )
        printf("%-32s OK\n", "gkq");
    else
        printf("%-32s FAILED(seed=%u)\n", "gkq", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testautogk, &result) )
        printf("%-32s OK\n", "autogk");
    else
        printf("%-32s FAILED(seed=%u)\n", "autogk", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testidwint, &result) )
        printf("%-32s OK\n", "idwint");
    else
        printf("%-32s FAILED(seed=%u)\n", "idwint", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testratint, &result) )
        printf("%-32s OK\n", "ratint");
    else
        printf("%-32s FAILED(seed=%u)\n", "ratint", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testpolint, &result) )
        printf("%-32s OK\n", "polint");
    else
        printf("%-32s FAILED(seed=%u)\n", "polint", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testspline1d, &result) )
        printf("%-32s OK\n", "spline1d");
    else
        printf("%-32s FAILED(seed=%u)\n", "spline1d", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testminlm, &result) )
        printf("%-32s OK\n", "minlm");
    else
        printf("%-32s FAILED(seed=%u)\n", "minlm", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testlsfit, &result) )
        printf("%-32s OK\n", "lsfit");
    else
        printf("%-32s FAILED(seed=%u)\n", "lsfit", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testpspline, &result) )
        printf("%-32s OK\n", "pspline");
    else
        printf("%-32s FAILED(seed=%u)\n", "pspline", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testspline2d, &result) )
        printf("%-32s OK\n", "spline2d");
    else
        printf("%-32s FAILED(seed=%u)\n", "spline2d", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testspdgevd, &result) )
        printf("%-32s OK\n", "spdgevd");
    else
        printf("%-32s FAILED(seed=%u)\n", "spdgevd", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testinverseupdate, &result) )
        printf("%-32s OK\n", "inverseupdate");
    else
        printf("%-32s FAILED(seed=%u)\n", "inverseupdate", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testschur, &result) )
        printf("%-32s OK\n", "schur");
    else
        printf("%-32s FAILED(seed=%u)\n", "schur", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testnleq, &result) )
        printf("%-32s OK\n", "nleq");
    else
        printf("%-32s FAILED(seed=%u)\n", "nleq", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testchebyshev, &result) )
        printf("%-32s OK\n", "chebyshev");
    else
        printf("%-32s FAILED(seed=%u)\n", "chebyshev", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testhermite, &result) )
        printf("%-32s OK\n", "hermite");
    else
        printf("%-32s FAILED(seed=%u)\n", "hermite", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testlaguerre, &result) )
        printf("%-32s OK\n", "laguerre");
    else
        printf("%-32s FAILED(seed=%u)\n", "laguerre", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testlegendre, &result) )
        printf("%-32s OK\n", "legendre");
    else
        printf("%-32s FAILED(seed=%u)\n", "legendre", (unsigned int)seed);
    fflush(stdout);
    if( call_unittest(seed, testalglibbasics, &result) )
        printf("%-32s OK\n", "alglibbasics");
    else
        printf("%-32s FAILED(seed=%u)\n", "alglibbasics", (unsigned int)seed);
    fflush(stdout);

    return result;
}

